получить ближайший номер из массива - PullRequest
127 голосов
/ 21 декабря 2011

У меня есть число от минус 1000 до плюс 1000, и у меня есть массив с числами в нем.Например:

[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]

Я хочу, чтобы число, которое я получил, изменилось на ближайший номер массива.

Например, я получаю 80 как число, которое я хочу получить82.

Ответы [ 16 ]

158 голосов
/ 09 октября 2013

ES5 Версия:

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);
137 голосов
/ 21 декабря 2011

Вот псевдокод, который должен быть преобразован в любой процедурный язык:

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

Он просто вычисляет абсолютные различия между данным числом и каждым элементом массива и возвращает один из них с минимальной разницей.

Для примеров значений:

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.

В качестве подтверждения концепции, вот код Python, который я использовал, чтобы показать это в действии:

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)

И, если вам действительно нужно это в Javascript, см. Ниже полный файл HTML, который демонстрирует функцию в действии:

<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>

Теперь имейте в виду, что могут быть возможности для повышения эффективности, если, например, ваши элементы данных отсортированы (это может быть выведено из примеров данных, но вы не указали это явно). Например, вы можете использовать двоичный поиск, чтобы найти ближайший элемент.

Вам также следует помнить, что, если вам не нужно это делать много раз в секунду, повышение эффективности будет в основном незаметным, если ваши наборы данных не станут на намного больше.

Если вы делаете хотите попробовать это таким образом (и можете гарантировать, что массив отсортирован в порядке возрастания), это хорошая отправная точка:

<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>

В основном он использует , заключающий в скобки и проверку среднего значения, чтобы уменьшить пространство решения наполовину для каждой итерации, классический алгоритм O(log N), тогда как последовательный поиск, приведенный выше, был 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

Как уже говорилось, это не должно иметь большого значения для небольших наборов данных или для вещей, которым не нужно , чтобы быть ослепительно быстрым, но это вариант, который вы, возможно, захотите рассмотреть.

31 голосов
/ 25 января 2016

ES6 (2015) Версия:

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

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

Для повторного использования вы можете использовать функцию карри, которая поддерживает заполнители (http://ramdajs.com/0.19.1/docs/#curry или https://lodash.com/docs#curry).. Это дает большую гибкость в зависимости от того, что вам нужно:

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

const closestTo5 = getClosest(_, 5);
const closestTo = getClosest([4, 9, 15, 6, 2]);
20 голосов
/ 21 декабря 2011

Рабочий код, как показано ниже:

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;
}
alert(closest(array,88));
14 голосов
/ 09 октября 2016

Работает с несортированными массивами

Несмотря на то, что здесь было опубликовано несколько хороших решений, JavaScript - это гибкий язык, который дает нам инструменты для решения проблемы различными способами. Конечно, все зависит от вашего стиля. Если ваш код более функциональный, вы найдете уменьшенный вариант подходящий, т. Е .:

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

Однако некоторым может показаться, что их трудно читать, в зависимости от их стиля кодирования. Поэтому я предлагаю новый способ решения проблемы:

  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

В отличие от других подходов нахождение минимального значения с использованием Math.min.apply, для этого не требуется сортировать входной массив arr . Нам не нужно заботиться об индексах или сортировать их заранее.

Я поясню код построчно для ясности:

  1. arr.map(function(k) { return Math.abs(k - x) }) Создает новый массив, в котором хранятся абсолютные значения заданных чисел (число в arr) за вычетом входного числа (x). Далее мы будем искать наименьшее число (которое также ближе всего к вводимому номеру)
  2. Math.min.apply(Math, indexArr) Это законный способ найти наименьшее число в массиве, который мы только что создали (ничего более)
  3. arr[indexArr.indexOf(min)] Это, пожалуй, самая интересная часть. Мы нашли наше наименьшее число, но мы не уверены, должны ли мы добавить или вычесть начальное число (x). Это потому, что мы использовали Math.abs(), чтобы найти разницу. Однако array.map создает (логически) карту входного массива, сохраняя индексы в том же месте. Поэтому, чтобы найти ближайшее число, мы просто возвращаем индекс найденного минимума в данном массиве indexArr.indexOf(min).

Я создал корзину , демонстрирующую его.

10 голосов
/ 01 августа 2014

Для отсортированных массивов (линейный поиск)

Пока все ответы сосредоточены на поиске по всему массиву.Учитывая, что ваш массив уже отсортирован, и вы действительно хотите только ближайшее число, это, вероятно, самое быстрое решение:

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

/**
* Returns the closest number from a sorted array.
**/
function closest(arr, target) {
    if (!(arr) || arr.length == 0)
        return null;
    if (arr.length == 1)
        return arr[0];

    for (var i=1; i<arr.length; i++) {
        // As soon as a number bigger than target is found, return the previous or current
        // number depending on which has smaller difference to the target.
        if (arr[i] > target) {
            var p = arr[i-1];
            var c = arr[i]
            return Math.abs( p-target ) < Math.abs( c-target ) ? p : c;
        }
    }
    // No number in array is bigger so return the last.
    return arr[arr.length-1];
}

// Trying it out
console.log( closest(arr, target) );

Обратите внимание, что алгоритм может быть значительно улучшен, например, с использованием двоичного дерева.

6 голосов
/ 12 декабря 2016

В этом решении используется ES5 экзистенциальный квантификатор Array#some, который позволяет остановить итерацию, если выполняется условие.

В противоположность Array#reduce, нет необходимости повторять все элементы для одного результата.

Внутри обратного вызова берется абсолютное delta между искомым значением и фактическим itemи по сравнению с последней дельтой.Если больше или равно, итерация останавливается, потому что все другие значения с их дельтами больше, чем фактическое значение.

Если delta в обратном вызове меньше, тогда фактический элемент назначается результату иdelta сохраняется в lastDelta.

Наконец, берутся меньшие значения с равными дельтами, как в приведенном ниже примере 22, что приводит к 2.

Если есть приоритет больших значений, дельта-проверка должна быть изменена с:

if (delta >= lastDelta) {

на:

if (delta > lastDelta) {
//       ^^^ without equal sign

Это получит с 22, результат 42 (Приоритет больших значений).

Эта функция требует отсортированных значений в массиве.


Код с приоритетом меньших значений:

function closestValue(array, value) {
    var result,
        lastDelta;

    array.some(function (item) {
        var delta = Math.abs(value - item);
        if (delta >= lastDelta) {
            return true;
        }
        result = item;
        lastDelta = delta;
    });
    return result;
}

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

console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 2  smaller value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82

Код с приоритетом больших значений:

function closestValue(array, value) {
    var result,
        lastDelta;

    array.some(function (item) {
        var delta = Math.abs(value - item);
        if (delta > lastDelta) {
            return true;
        }
        result = item;
        lastDelta = delta;
    });
    return result;
}

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

console.log(21, closestValue(data, 21)); //  2
console.log(22, closestValue(data, 22)); // 42 greater value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
4 голосов
/ 16 мая 2019

Все решения чрезмерно спроектированы.

Это так же просто, как:

const needle = 5;
const haystack = [1, 2, 3, 4, 5, 6, 7, 8, 9];

haystack.sort((a, b) => {
  return Math.abs(a - needle) - Math.abs(b - needle);
});

// 5
3 голосов
/ 05 апреля 2014

Я не знаю, должен ли я ответить на старый вопрос, но так как это сообщение появляется первым в поиске Google, я надеялся, что вы простите мне добавление моего решения и моего 2c здесь.

Будучи ленивым, я не мог поверить, что решением этого вопроса будет LOOP, поэтому я поискал немного больше и вернулся с функцией фильтра :

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

function BiggerThan(inArray) {
  return inArray > myValue;
}

var arrBiggerElements = myArray.filter(BiggerThan);
var nextElement = Math.min.apply(null, arrBiggerElements);
alert(nextElement);

Вот и все!

2 голосов
/ 01 марта 2016

Мне нравится подход от Fusion, но в нем есть небольшая ошибка.Вот как это правильно:

    function closest(array, number) {
        var num = 0;
        for (var i = array.length - 1; i >= 0; i--) {
            if(Math.abs(number - array[i]) < Math.abs(number - array[num])){
                num = i;
            }
        }
        return array[num];
    }

Это также немного быстрее, потому что он использует улучшенный цикл for.

В конце я написал свою функцию так:

    var getClosest = function(number, array) {
        var current = array[0];
        var difference = Math.abs(number - current);
        var index = array.length;
        while (index--) {
            var newDifference = Math.abs(number - array[index]);
            if (newDifference < difference) {
                difference = newDifference;
                current = array[index];
            }
        }
        return current;
    };

Я протестировал его с console.time(), и он немного быстрее, чем другая функция.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...