Get the closest number out of an array

Solution 1:

ES5 Version:

var counts = [4, 9, 15, 6, 2],
  goal = 5;

var closest = counts.reduce(function(prev, curr) {
  return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});

console.log(closest);

Solution 2:

Here's the pseudo-code which should be convertible into any procedural language:

array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)

def closest (num, arr):
    curr = arr[0]
    foreach val in arr:
        if abs (num - val) < abs (num - curr):
            curr = val
    return curr

It simply works out the absolute differences between the given number and each array element and gives you back one of the ones with the minimal difference.

For the example values:

number = 112  112  112  112  112  112  112  112  112  112
array  =   2   42   82  122  162  202  242  282  322  362
diff   = 110   70   30   10   50   90  130  170  210  250
                         |
                         +-- one with minimal absolute difference.

As a proof of concept, here's the Python code I used to show this in action:

def closest (num, arr):
    curr = arr[0]
    for index in range (len (arr)):
        if abs (num - arr[index]) < abs (num - curr):
            curr = arr[index]
    return curr

array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)

And, if you really need it in Javascript, see below for a complete HTML file which demonstrates the function in action:

<html>
    <head></head>
    <body>
        <script language="javascript">
            function closest (num, arr) {
                var curr = arr[0];
                var diff = Math.abs (num - curr);
                for (var val = 0; val < arr.length; val++) {
                    var newdiff = Math.abs (num - arr[val]);
                    if (newdiff < diff) {
                        diff = newdiff;
                        curr = arr[val];
                    }
                }
                return curr;
            }
            array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
            number = 112;
            alert (closest (number, array));
        </script>
    </body>
</html>

Now keep in mind there may be scope for improved efficiency if, for example, your data items are sorted (that could be inferred from the sample data but you don't explicitly state it). You could, for example, use a binary search to find the closest item.

You should also keep in mind that, unless you need to do it many times per second, the efficiency improvements will be mostly unnoticable unless your data sets get much larger.

If you do want to try it that way (and can guarantee the array is sorted in ascending order), this is a good starting point:

<html>
    <head></head>
    <body>
        <script language="javascript">
            function closest (num, arr) {
                var mid;
                var lo = 0;
                var hi = arr.length - 1;
                while (hi - lo > 1) {
                    mid = Math.floor ((lo + hi) / 2);
                    if (arr[mid] < num) {
                        lo = mid;
                    } else {
                        hi = mid;
                    }
                }
                if (num - arr[lo] <= arr[hi] - num) {
                    return arr[lo];
                }
                return arr[hi];
            }
            array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
            number = 112;
            alert (closest (number, array));
        </script>
    </body>
</html>

It basically uses bracketing and checking of the middle value to reduce the solution space by half for each iteration, a classic O(log N) algorithm whereas the sequential search above was O(N):

0  1  2   3   4   5   6   7   8   9  <- indexes
2 42 82 122 162 202 242 282 322 362  <- values
L             M                   H  L=0, H=9, M=4, 162 higher, H<-M
L     M       H                      L=0, H=4, M=2, 82 lower/equal, L<-M
      L   M   H                      L=2, H=4, M=3, 122 higher, H<-M
      L   H                          L=2, H=3, difference of 1 so exit
          ^
          |
          H (122-112=10) is closer than L (112-82=30) so choose H

As stated, that shouldn't make much of a difference for small datasets or for things that don't need to be blindingly fast, but it's an option you may want to consider.

Solution 3:

ES6 (ECMAScript 2015) Version:

const counts = [4, 9, 15, 6, 2];
const goal = 5;

const output = counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);

console.log(output);

For reusability you can wrap in a curry function that supports placeholders (http://ramdajs.com/0.19.1/docs/#curry or https://lodash.com/docs#curry). This gives lots of flexibility depending on what you need:

const getClosest = _.curry((counts, goal) => {
  return counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});

const closestToFive = getClosest(_, 5);
const output = closestToFive([4, 9, 15, 6, 2]);

console.log(output);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Solution 4:

Working code as below:

var array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];

function closest(array, num) {
  var i = 0;
  var minDiff = 1000;
  var ans;
  for (i in array) {
    var m = Math.abs(num - array[i]);
    if (m < minDiff) {
      minDiff = m;
      ans = array[i];
    }
  }
  return ans;
}
console.log(closest(array, 88));

Solution 5:

Works with unsorted arrays

While there were some good solutions posted here, JavaScript is a flexible language that gives us tools to solve a problem in many different ways. It all comes down to your style, of course. If your code is more functional, you'll find the reduce variation suitable, i.e.:

  arr.reduce(function (prev, curr) {
    return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
  });

However, some might find that hard to read, depending on their coding style. Therefore I propose a new way of solving the problem:

  var findClosest = function (x, arr) {
    var indexArr = arr.map(function(k) { return Math.abs(k - x) })
    var min = Math.min.apply(Math, indexArr)
    return arr[indexArr.indexOf(min)]
  }

  findClosest(80, [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]) // Outputs 82

Contrary to other approaches finding the minimum value using Math.min.apply, this one doesn't require the input array arr to be sorted. We don't need to care about the indexes or sort it beforehand.

I'll explain the code line by line for clarity:

  1. arr.map(function(k) { return Math.abs(k - x) }) Creates a new array, essentially storing the absolute values of the given numbers (number in arr) minus the input number (x). We'll look for the smallest number next (which is also the closest to the input number)
  2. Math.min.apply(Math, indexArr) This is a legit way of finding the smallest number in the array we've just created before (nothing more to it)
  3. arr[indexArr.indexOf(min)] This is perhaps the most interesting part. We have found our smallest number, but we're not sure if we should add or subtract the initial number (x). That's because we used Math.abs() to find the difference. However, array.map creates (logically) a map of the input array, keeping the indexes in the same place. Therefore, to find out the closest number we just return the index of the found minimum in the given array indexArr.indexOf(min).

I've created a bin demonstrating it.