DEV Community

Michael Dayah for Ptable

Posted on

Find nearest numeric values

I wasn't a fan of functional programming when I first saw it. It seemed like clever overengineering and trading simple, procedural code for complex one-liners. I had just gotten used to not trying to be clever and creating temporary variables just for future me to understand what was going on.

However, I mostly go by intuition when deciding whether to further optimize. If the little voice in my head insists there's a better way of doing something, it won't let go until I find it. This particular problem is especially well-suited to functional programming.

My goal is to find the value or values numerically closest to a given search value. Since there may be more than one, this requires two passes. The first pass is to find the smallest difference between the search and all searched values. Then, on the second pass, every value that close is considered a match. I do this against a Map() and push keys onto matches, but for simplicity it's shown with a bare array.

const haystack = [1, 2, 2, 3, 4, 5, 6];
const search = 2.5;
let matches = [];
let closest = Number.MAX_VALUE;
for (const value of haystack) {
    const difference = Math.abs(value - search);
    if (difference < closest)
        closest = difference;
}

for (const value of haystack) {
    const difference = Math.abs(value - search);
    if (difference <= closest)
        matches.push(value);
}

matches;
<- [2, 2, 3]

At the very least, each pass on its own can be made functional. Array.reduce() is a good candidate for finding the closest value, since it's reducing an array from many elements to one. We pass in Number.MAX_VALUE again as the initial value to ensure a search far out of our range still creates a match. Each successive pass uses Math.min() to find successively closer values. Then, a simple Array.filter() can traverse the haystack and return an array with values nearest the numeric search.

let closest = haystack.reduce((closest, current) =>
    Math.min(Math.abs(current - search), closest),
    Number.MAX_VALUE
);

matches = haystack.filter(value =>
    Math.abs(value - search) <= closest
);
<- [2, 2, 3]

Since closest only appears once in the second block, it's technically possible to stuff all of the first block into the second and make it a true one-liner. Resist the temptation as that would cause it to find the closest value over and over again for every element of the array, unless static analysis has come a long way.

Top comments (0)