Как я могу легко получить элемент min или max массива JavaScript?
Пример Psedocode:
let array = [100, 0, 50]
array.min() //=> 0
array.max() //=> 100
Как я могу легко получить элемент min или max массива JavaScript?
Пример Psedocode:
let array = [100, 0, 50]
array.min() //=> 0
array.max() //=> 100
Как насчет расширения встроенного объекта 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 );
var max_of_array = Math.max.apply(Math, array);
Для полного обсуждения см.: http://aaroncrane.co.uk/2008/11/javascript_max_api/
Для больших массивов (~ 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;
};
Использование оператора распространения (ES6)
Math.max(...array); // the same with "min" => Math.min(...array);
const array = [10, 2, 33, 4, 5];
console.log(
Math.max(...array)
)
// 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 по 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;
}
}
Если вы параноик, как я, об использовании 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
на них.
.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']
Вы делаете это, расширяя тип массива:
Array.max = function( array ){
return Math.max.apply( Math, array );
};
Array.min = function( array ){
return Math.min.apply( Math, array );
};
Повышен с здесь (от Джона Ресига)
Простое решение, чтобы найти минимальное значение по сравнению с 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
.
Два способа короче и проще:
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);
});
Другие уже дали некоторые решения, в которых они дополняют 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
будет работать без проблем.
Еще один способ сделать это:
var arrayMax = Function.prototype.apply.bind(Math.max, null);
Использование:
var max = arrayMax([2, 5, 1]);
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Math/max
function getMaxOfArray(numArray) {
return Math.max.apply(null, numArray);
}
var arr = [100, 0, 50];
console.log(getMaxOfArray(arr))
Я удивлен, что не упоминается функция уменьшения.
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]
Это может удовлетворить ваши цели.
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;
}
Для больших массивов (~ 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;
};
У меня была та же проблема, мне нужно было получить минимальное и максимальное значения массива, и, к моему удивлению, встроенных функций для массивов не было. Прочитав много, я решил самостоятельно протестировать "лучшие 3" решения:
Проверочный код:
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(). Результатом этого было три цифры и стандартное отклонение:
Решение 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).
Вы можете использовать следующую функцию в любом месте вашего проекта:
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
Следующий код работает для меня:
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); });
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 мс.
Идите, следите за ходом.
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);
Я думал, что разделю мое простое и понятное решение.
Для 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);
Здесь один из способов получить максимальное значение из массива объектов. Создайте копию (с фрагментом), затем отсортируйте копию в порядке убывания и возьмите первый элемент.
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;
Простые вещи, действительно.
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());
Использование 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
Решение ChaosPandion работает, если вы используете protoype. Если нет, рассмотрите это:
Array.max = function( array ){
return Math.max.apply( Math, array );
};
Array.min = function( array ){
return Math.min.apply( Math, array );
};
Приведенное выше вернет NaN, если значение массива не является целым числом, поэтому вы должны создать некоторую функциональность, чтобы этого избежать. В противном случае это сработает.
Если вы используете инфраструктуру prototype.js, тогда этот код будет работать нормально:
arr.min();
arr.max();
Документировано здесь: Структура прототипа Javascript для max
Если вы используете библиотеку 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 }
let arr = [2,5,3,5,6,7,1];
let max = Math.max(...arr); // 7
let min = Math.min(...arr); // 1
Попробуйте
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))