Найти элемент min/max массива в JavaScript

Как я могу легко получить элемент min или max массива JavaScript?

Пример Psedocode:

let array = [100, 0, 50]

array.min() //=> 0
array.max() //=> 100

Ответ 1

Как насчет расширения встроенного объекта Array для использования Math.max/Math.min:

Array.prototype.max = function() {
  return Math.max.apply(null, this);
};

Array.prototype.min = function() {
  return Math.min.apply(null, this);
};

Вот JSFiddle.

Увеличение встроенных вложений может вызвать конфликты с другими библиотеками (некоторые видят), поэтому вам может быть удобнее всего с помощью apply 'ing Math.xxx() непосредственно в ваш массив:

var min = Math.min.apply(null, arr),
    max = Math.max.apply(null, arr);

В качестве альтернативы, если ваш браузер поддерживает ECMAScript 6, вы можете использовать оператор распространения, который работает аналогично методу apply:

var min = Math.min( ...arr ),
    max = Math.max( ...arr );

Ответ 3

Для больших массивов (~ 10⁷ элементов) Math.min и Math.max обе приводят к ошибке в Node.js.

RangeError: превышен максимальный размер стека вызовов

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

function arrayMin(arr) {
  return arr.reduce(function (p, v) {
    return ( p < v ? p : v );
  });
}

function arrayMax(arr) {
  return arr.reduce(function (p, v) {
    return ( p > v ? p : v );
  });
}

Если вас беспокоит скорость, следующий код ~ 3 раза быстрее, чем Math.max.apply на моем компьютере. См. http://jsperf.com/min-and-max-in-array/2.

function arrayMin(arr) {
  var len = arr.length, min = Infinity;
  while (len--) {
    if (arr[len] < min) {
      min = arr[len];
    }
  }
  return min;
};

function arrayMax(arr) {
  var len = arr.length, max = -Infinity;
  while (len--) {
    if (arr[len] > max) {
      max = arr[len];
    }
  }
  return max;
};

Если ваши массивы содержат строки вместо чисел, вам также необходимо принудить их к числам. Этот код делает это, но он замедляет код до ~ 10 раз на моей машине. См. http://jsperf.com/min-and-max-in-array/3.

function arrayMin(arr) {
  var len = arr.length, min = Infinity;
  while (len--) {
    if (Number(arr[len]) < min) {
      min = Number(arr[len]);
    }
  }
  return min;
};

function arrayMax(arr) {
  var len = arr.length, max = -Infinity;
  while (len--) {
    if (Number(arr[len]) > max) {
      max = Number(arr[len]);
    }
  }
  return max;
};

Ответ 4

Использование оператора распространения (ES6)

Math.max(...array);  // the same with "min" => Math.min(...array);

const array = [10, 2, 33, 4, 5];

console.log(
  Math.max(...array)
)

Ответ 5

ТЛ; др

// For regular arrays:
var max = Math.max(...arrayOfNumbers);

// For arrays with tens of thousands of items:
let max = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
  if (testArray[i] > max) {
    max = testArray[i];
  }
}

Решение MDN

Официальные документы MDN по Math.max() уже охватывают эту проблему:

Следующая функция использует Function.prototype.apply(), чтобы найти максимальный элемент в числовом массиве. getMaxOfArray([1, 2, 3]) эквивалентен Math.max(1, 2, 3), но вы можете использовать getMaxOfArray() для программно построенных массивов любого размера.

function getMaxOfArray(numArray) {
    return Math.max.apply(null, numArray);
}

Или с новым оператором распространения, получить максимум массива становится намного проще.

var arr = [1, 2, 3];
var max = Math.max(...arr);

Максимальный размер массива

Согласно MDN, apply и распространение решений имеет ограничение 65536, которое исходит из ограничения максимального количества аргументов:

Но будьте осторожны: при использовании применять этот способ вы рискуете превысить ограничение длины аргумента движка JavaScript. Последствия применения функции со слишком большим количеством аргументов (например, более десятков тысяч аргументов) варьируются в зависимости от движков (JavaScriptCore имеет жестко запрограммированный предел аргументов 65536), потому что это предел (в действительности, даже характер любого чрезмерно большого стека) поведение) не уточняется. Некоторые двигатели будут выбрасывать исключения. Более пагубно, другие будут произвольно ограничивать количество аргументов, фактически передаваемых прикладной функции. Чтобы проиллюстрировать этот последний случай: если бы такой механизм имел ограничение в четыре аргумента (фактические пределы, конечно, значительно выше), это было бы так, как если бы аргументы 5, 6, 2, 3 были переданы для применения в приведенных выше примерах, а не полный массив.

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

В 2019 году фактическим пределом является максимальный размер стека вызовов. Для современных настольных браузеров на базе Chromium это означает, что когда дело доходит до нахождения минимума/максимума с apply или распространением, практически максимальный размер для массивов только с числами составляет ~ 120000. Выше этого будет переполнение стека, и будет выдана следующая ошибка:

RangeError: превышен максимальный размер стека вызовов

С помощью приведенного ниже сценария (на основе этого сообщения в блоге), уловив эту ошибку, вы можете рассчитать предел для вашей конкретной среды.

Предупреждение! Запуск этого скрипта занимает много времени, и в зависимости от производительности вашей системы он может замедлить или привести к сбою вашего браузера/системы!

let testArray = Array.from({length: 10000}, () => Math.floor(Math.random() * 2000000));
for (i = 10000; i < 1000000; ++i) {
  testArray.push(Math.floor(Math.random() * 2000000));
  try {
    Math.max.apply(null, testArray);
  } catch (e) {
    console.log(i);
    break;
  }
}

Ответ 6

Если вы параноик, как я, об использовании Math.max.apply (что может привести к ошибкам при выдаче больших массивов в соответствии с MDN), попробуйте это

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.max(a, b);
  });
}

function arrayMin(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  });
}

Или, в ES6:

function arrayMax(array) {
  return array.reduce((a, b) => Math.max(a, b));
}

function arrayMin(array) {
  return array.reduce((a, b) => Math.min(a, b));
}

Анонимные функции, к сожалению, необходимы (вместо использования Math.max.bind(Math), потому что reduce не просто передает a и b в свою функцию, но также i и ссылку на сам массив, поэтому мы должны обеспечить, чтобы мы не пытались называть max на них.

Ответ 7

.apply часто используется, когда намерение состоит в том, чтобы вызвать переменную функцию со списком значений аргументов, например.

Функция Math.max([value1[,value2, ...]]) возвращает наибольшее число из нуля или более.

Math.max(10, 20); // 20
Math.max(-10, -20); // -10
Math.max(-10, 20); // 20

Метод Math.max() не позволяет передавать массив. Если у вас есть список значений, которые необходимо получить самому большому, вы обычно вызываете эту функцию с помощью Function.prototype.apply(), например.

Math.max.apply(null, [10, 20]); // 20
Math.max.apply(null, [-10, -20]); // -10
Math.max.apply(null, [-10, 20]); // 20

Однако, с ECMAScript 6 вы можете использовать оператор распространения:

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

Используя оператор спреда, вышесказанное можно переписать как таковое:

Math.max(...[10, 20]); // 20
Math.max(...[-10, -20]); // -10
Math.max(...[-10, 20]); // 20

При вызове функции с использованием вариационного оператора вы можете даже добавить дополнительные значения, например.

Math.max(...[10, 20], 50); // 50
Math.max(...[-10, -20], 50); // 50

Bonus:

Оператор Spread позволяет использовать синтаксис литерала массива для создания новых массивов в ситуациях, когда в ES5 вам нужно будет вернуться к императивному коду, используя комбинацию push, splice и т.д.

let foo = ['b', 'c'];
let bar = ['a', ...foo, 'd', 'e']; // ['a', 'b', 'c', 'd', 'e']

Ответ 8

Вы делаете это, расширяя тип массива:

Array.max = function( array ){
    return Math.max.apply( Math, array );
};
Array.min = function( array ){
    return Math.min.apply( Math, array );
}; 

Повышен с здесь (от Джона Ресига)

Ответ 9

Простое решение, чтобы найти минимальное значение по сравнению с Array элементов является использование Array прототипа функции reduce:

A = [4,3,-9,-2,2,1];
A.reduce((min, val) => val < min ? val : min, A[0]); // returns -9

или используя встроенную в JavaScript функцию Math.Min() (спасибо @Tenflex):

A.reduce((min,val) => Math.min(min,val), A[0]);

Это устанавливает min в A[0], а затем проверяет A[1]...A[n], строго ли оно меньше текущего min. Если A[i] < min то min обновляется до A[i]. Когда все элементы массива обработаны, в качестве результата возвращается min.

Ответ 10

Два способа короче и проще:

let arr = [2, 6, 1, 0]

// Way 1:
let max = Math.max.apply(null, arr)

//Way 2:
let max = arr.reduce(function(a, b) {
    return Math.max(a, b);
});

Ответ 11

Другие уже дали некоторые решения, в которых они дополняют Array.prototype. Все, что я хочу в этом ответе, это выяснить, должно ли оно быть Math.min.apply( Math, array ) или Math.min.apply( null, array ). Итак, какой контекст должен использоваться, Math или null?

При передаче null в качестве контекста в apply контекст по умолчанию будет глобальным объектом (объект window в случае браузеров). Передача объекта Math в качестве контекста будет правильным решением, но это не повредит при прохождении null. Здесь пример, когда null может вызвать проблемы при декорировании функции Math.max:

// decorate Math.max
(function (oldMax) {
    Math.max = function () {
        this.foo(); // call Math.foo, or at least that what we want

        return oldMax.apply(this, arguments);
    };
})(Math.max);

Math.foo = function () {
    print("foo");
};

Array.prototype.max = function() {
  return Math.max.apply(null, this); // <-- passing null as the context
};

var max = [1, 2, 3].max();

print(max);

Вышеизложенное исключает исключение, поскольку this.foo будет оцениваться как window.foo, которое равно undefined. Если мы заменим null на Math, все будет работать так, как ожидалось, и строка "foo" будет напечатана на экране (я тестировал это с помощью Mozilla Rhino).

Вы можете в значительной степени предположить, что никто не украсил Math.max, поэтому передача null будет работать без проблем.

Ответ 12

Еще один способ сделать это:

var arrayMax = Function.prototype.apply.bind(Math.max, null);

Использование:

var max = arrayMax([2, 5, 1]);

Ответ 14

Я удивлен, что не упоминается функция уменьшения.

var arr = [1, 10, 5, 11, 2]

var b = arr.reduce(function(previous,current){ 
                      return previous > current ? previous:current
                   });

b => 11
arr => [1, 10, 5, 11, 2]

Ответ 15

Это может удовлетворить ваши цели.

Array.prototype.min = function(comparer) {

    if (this.length === 0) return null;
    if (this.length === 1) return this[0];

    comparer = (comparer || Math.min);

    var v = this[0];
    for (var i = 1; i < this.length; i++) {
        v = comparer(this[i], v);    
    }

    return v;
}

Array.prototype.max = function(comparer) {

    if (this.length === 0) return null;
    if (this.length === 1) return this[0];

    comparer = (comparer || Math.max);

    var v = this[0];
    for (var i = 1; i < this.length; i++) {
        v = comparer(this[i], v);    
    }

    return v;
}

Ответ 16

Для больших массивов (~ 10⁷ элементов) Math.min и Math.max выводит RangeError (Максимальный размер стека вызовов) в node.js.

Для больших массивов быстрое и грязное решение:

Array.prototype.min = function() {
    var r = this[0];
    this.forEach(function(v,i,a){if (v<r) r=v;});
    return r;
};

Ответ 17

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

  • дискретное решение: цикл FOR для проверки каждого элемента массива на текущее значение max и/или min;
  • Решение APPLY: отправка массива в внутренние функции Math.max и/или Math.min с использованием apply (null, array);
  • Решение REDUCE: рекурсия проверки на каждый элемент массива с использованием функции сокращения().

Проверочный код:

function GetMaxDISCRETE(A)
{   var MaxX=A[0];

    for (var X=0;X<A.length;X++)
        if (MaxX<A[X])
            MaxX=A[X];

    return MaxX;
}

function GetMaxAPPLY(A)
{   return Math.max.apply(null,A);
}

function GetMaxREDUCE(A)
{   return A.reduce(function(p,c)
    {   return p>c?p:c;
    });
}

Массив A был заполнен 100 000 случайными целыми числами, каждая функция была выполнена 10 000 раз в Mozilla Firefox 28.0 на настольном компьютере Intel Pentium 4 2.99GHz с Windows Vista. Время в секундах, полученное функцией performance.now(). Результатом этого было три цифры и стандартное отклонение:

  • Дискретное решение: среднее = 0.161s, sd = 0.078
  • APPLY solution: mean = 3.571s, sd = 0.487
  • Решение REDUCE: среднее value = 0,350 с, sd = 0,044

Решение REDUCE было на 117% медленнее, чем дискретное решение. Решение APPLY было хуже, на 2,118% медленнее, чем дискретное решение. Кроме того, как заметил Петр, он не работает для больших массивов (около более 1 000 000 элементов).

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

var MaxX=A[0],MinX=A[0];

for (var X=0;X<A.length;X++)
{   if (MaxX<A[X])
        MaxX=A[X];
    if (MinX>A[X])
        MinX=A[X];
}

Время: среднее value = 0,218 с, sd = 0,094

Таким образом, он на 35% медленнее, чем простое дискретное решение, но он извлекает как максимальное, так и минимальное значения сразу (любое другое решение займет как минимум вдвое больше, чем для их получения). После того, как OP понадобится оба значения, дискретное решение будет лучшим выбором (даже если две отдельные функции, одна для вычисления максимума и другая для вычисления минимума, они превзойдут второе лучшее решение REDUCE).

Ответ 18

Вы можете использовать следующую функцию в любом месте вашего проекта:

function getMin(array){
    return Math.min.apply(Math,array);
}

function getMax(array){
    return Math.max.apply(Math,array);
}

И тогда вы можете вызвать функции, передающие массив:

var myArray = [1,2,3,4,5,6,7];
var maximo = getMax(myArray); //return the highest number

Ответ 19

Следующий код работает для меня:

var valueList = [10,4,17,9,3];
var maxValue = valueList.reduce(function(a, b) { return Math.max(a, b); });
var minValue = valueList.reduce(function(a, b) { return Math.min(a, b); });

Ответ 20

Math.min & Math.max

Math.min и Math.max - это рекурсивные операции, которые, скорее всего, требуют больших массивов (больше чем ~ 10⁷ элементов).

Вместо этого вы можете использовать старые циклы JavaScript следующим образом:

function getMinMax(arr) {
    return arr.reduce(({min, max}, v) => ({
        min: min < v ? min : v,
        max: max > v ? max : v,
    }), { min: arr[0], max: arr[0] });
}

Или (лучше во время выполнения):

function getMinMax(arr) {
    let min = arr[0];
    let max = arr[0];
    let i = arr.length;

    while (i--) {
        min = arr[i] < min ? arr[i] : min;
        max = arr[i] > max ? arr[i] : max;
    }
    return { min, max };
}

* Протестировано с 1 000 000 предметов:
Для справки: время выполнения 1-й функции (на моей машине) составило 15,84 мс против 2-й функции с 4,32 мс.

Ответ 21

Идите, следите за ходом.

var min = null;
var max = null;
for (var i = 0, len = arr.length; i < len; ++i)
{
    var elem = arr[i];
    if (min === null || min > elem) min = elem;
    if (max === null || max < elem) max = elem;
}
alert( "min = " + min + ", max = " + max );

Это оставит min/max null, если в массиве нет элементов. Будет устанавливать min и max за один проход, если массив имеет какие-либо элементы.

Вы также можете расширить Array с помощью метода range, используя приведенное выше, чтобы разрешить повторное использование и улучшить читаемость. См. Рабочую скрипту в http://jsfiddle.net/9C9fU/

Array.prototype.range = function() {

    var min = null,
        max = null,
        i, len;

    for (i = 0, len = this.length; i < len; ++i)
    {
        var elem = this[i];
        if (min === null || min > elem) min = elem;
        if (max === null || max < elem) max = elem;
    }

    return { min: min, max: max }
};

Используется как

var arr = [3, 9, 22, -7, 44, 18, 7, 9, 15];

var range = arr.range();

console.log(range.min);
console.log(range.max);

Ответ 22

Я думал, что разделю мое простое и понятное решение.

Для min:

var arr = [3, 4, 12, 1, 0, 5];
var min = arr[0];
for (var k = 1; k < arr.length; k++) {
  if (arr[k] < min) {
    min = arr[k];
  }
}
console.log("Min is: " + min);

Ответ 23

Здесь один из способов получить максимальное значение из массива объектов. Создайте копию (с фрагментом), затем отсортируйте копию в порядке убывания и возьмите первый элемент.

var myArray = [
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
]

maxsort = myArray.slice(0).sort(function(a, b) { return b.ID - a.ID })[0].ID; 

Ответ 24

Простые вещи, действительно.

var arr = [10,20,30,40];
arr.max = function() { return  Math.max.apply(Math, this); }; //attach max funct
arr.min = function() { return  Math.min.apply(Math, this); }; //attach min funct

alert("min: " + arr.min() + " max: " + arr.max());

Ответ 25

Использование Math.max() или Math.min()

Math.max(10, 20);   //  20
Math.min(-10, -20); // -20

Следующая функция использует Function.prototype.apply() для нахождения максимального элемента в числовом массиве. getMaxOfArray([1, 2, 3]) эквивалентен Math.max(1, 2, 3), но вы можете использовать getMaxOfArray() для программно сконструированных массивов любого размера.

function getMaxOfArray(numArray) {
  return Math.max.apply(null, numArray);
}

Или с новым оператором распространения, получение максимума массива становится намного проще.

var arr = [1, 2, 3];
var max = Math.max(...arr); // 3
var min = Math.min(...arr); // 1

Ответ 26

Решение ChaosPandion работает, если вы используете protoype. Если нет, рассмотрите это:

Array.max = function( array ){
    return Math.max.apply( Math, array );
};

Array.min = function( array ){
    return Math.min.apply( Math, array );
};

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

Ответ 27

Если вы используете инфраструктуру prototype.js, тогда этот код будет работать нормально:

arr.min();
arr.max();

Документировано здесь: Структура прототипа Javascript для max

Ответ 28

Если вы используете библиотеку sugar.js, вы можете написать arr.min() и arr.max(), как вы предлагаете. Вы также можете получать значения min и max из нечисловых массивов.

min (map, all = false) Возвращает элемент в массиве с помощью самое низкое значение. map может быть функцией, отображающей проверяемое значение или строка, действующая как ярлык. Если все верно, вернет все мин. значения в массиве.

max (map, all = false) Возвращает элемент в массиве с помощью наибольшее значение. map может быть функцией, отображающей проверяемое значение или строка, действующая как ярлык. Если все верно, вернет все макс. значения в массиве.

Примеры:

[1,2,3].min() == 1
['fee','fo','fum'].min('length') == "fo"
['fee','fo','fum'].min('length', true) == ["fo"]
['fee','fo','fum'].min(function(n) { return n.length; }); == "fo"
[{a:3,a:2}].min(function(n) { return n['a']; }) == {"a":2}
['fee','fo','fum'].max('length', true) == ["fee","fum"]

Библиотеки, такие как Lo-Dash и underscore.js также предоставляют аналогичные мощные функции min и max:

Пример из Lo-Dash:

_.max([4, 2, 8, 6]) == 8
var characters = [
  { 'name': 'barney', 'age': 36 },
  { 'name': 'fred',   'age': 40 }
];
_.max(characters, function(chr) { return chr.age; }) == { 'name': 'fred', 'age': 40 }

Ответ 29

let arr = [2,5,3,5,6,7,1];

let max = Math.max(...arr); // 7
let min = Math.min(...arr); // 1

Ответ 30

Попробуйте

let max= a=> a.reduce((m,x)=> m>x ? m:x);
let min= a=> a.reduce((m,x)=> m<x ? m:x);

let max= a=> a.reduce((m,x)=> m>x ? m:x);
let min= a=> a.reduce((m,x)=> m<x ? m:x);

// TEST - pixel buffer
let arr = Array(200*800*4).fill(0); 
arr.forEach((x,i)=> arr[i]=100-i%101); 

console.log('Max', max(arr));
console.log('Min', min(arr))