Answers:
如何扩充内置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
读取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 );
null
或Math
或{}
或任何以apply()
或call()
对结果没有影响。Math.max
不也不应在this
内部引用。
Math.max.apply(null, $.makeArray(array));
.max
or .min
方法的潜力有关。完全现实的方案:您使用此答案。在2016年,ES7或ES8规范Array.max
和Array.min
。与该版本不同,它们在字符串上工作。您未来的同事尝试使用现在有据可查的本机.max()
方法获取按字母顺序排列的最新字符串,但是神秘地得到NaN
。数小时后,她找到了此代码,运行git blame
,然后诅咒您的名字。
var max_of_array = Math.max.apply(Math, array);
有关完整的讨论,请参见:http : //aaroncrane.co.uk/2008/11/javascript_max_api/
Math.max.apply(Math, array)
和之间有什么区别Math.max.apply(null, array)
?该博客说:“ ...您还必须重复说一遍max
属于Math
...的内容”,但看来我不必这样做(通过将第一个参数设置为apply
as null
)。
Math.max(a,b)
,Math
将作为this
值传递,因此使用时进行相同的操作可能很有意义apply
。但是Math.max
不使用该this
值,因此您可以传递所需的任何值。
对于大阵列(〜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 );
});
}
如果您担心速度,下面的代码将比Math.max.apply
我的计算机快3倍。参见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;
};
min
和max
最后一个元素,并将迭代次数减少1(while(--len)
);)
very different results
你这样做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文档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;
}
}
基于EscapeNetscape注释中的测试,我创建了一些基准测试,该基准对具有100000个项目的仅随机数数组测试5种不同的方法。
在2019年,结果显示标准循环(BTW没有大小限制)是最快的。apply
传播紧随其后,然后是MDN的混合解决方案,后来才reduce
成为最慢的。
几乎所有测试都给出了相同的结果,唯一不同的是,由于某种原因传播速度最慢。
如果您增加阵列以拥有一百万个项目,那么事情就会开始崩溃,而您会留下标准循环作为快速解决方案, reduce
较慢的。
Math.max.apply(Math, arr)
具有“最大”兼容性。
(...)
,apply
则两者都将传播并失败或返回错误的结果。reduce的解决方案没有此问题。”测试Chrome,FF,Edge和IE11似乎最多可以包含100k个值的数组。(在Win10和最新的浏览器上测试:Chrome 110k,Firefox 300k,Edge 400k,IE11 150k)。
如果您像我一样使用偏执狂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
它们。
Math.max(...array)
?
function arrayMax(array) { return array.reduce(function(a, b) { return Math.max(a, b); }); // <--------- missing ) }
Math.min()
,不带值调用,返回Infinity
,所以这些函数可以reduce(..., Infinity)
用来匹配该行为。我更喜欢它抛出一个异常(如当前那样),因为采取空数组的最小值似乎是一个错误。
.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
奖金:
价差操作,您可以使用数组文本语法在ES5中,你将需要回落到必要的代码的情况下创建新的阵列,使用的组合push
,splice
等等。
let foo = ['b', 'c'];
let bar = ['a', ...foo, 'd', 'e']; // ['a', 'b', 'c', 'd', 'e']
concat
将由大多数程序员编写,因为它使您可以保持单一线条样式。
两种方法更短,更容易:
let arr = [2, 6, 1, 0]
方式1:
let max = Math.max.apply(null, arr)
方式二:
let max = arr.reduce(function(a, b) {
return Math.max(a, b);
});
0
可以使用[0].concat(arr)
或使用扩展语法[0, ...arr]
(代替“ arr”)
找到一个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
完所有数组元素后,将返回结果。
编辑:包括最小值的位置:
A = [4,3,-9,-2,2,1];
A.reduce((min, val) => val < min._min ? {_min: val, _idx: min._curr, _curr: min._curr + 1} : {_min: min._min, _idx: min._idx, _curr: min._curr + 1}, {_min: A[0], _idx: 0, _curr: 0}); // returns { _min: -9, _idx: 2, _curr: 6 }
min
返回值,还要返回它在数组中的位置?
其他人已经给出了一些扩展解决方案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's 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
将毫无问题。
Foo.staticMethod
和引用this
?这不是装饰器设计中的错误吗?(当然,除非他们要引用全局范围,并且要保持独立于所使用的JavaScript引擎,例如Rhino)。
另一种方法是:
var arrayMax = Function.prototype.apply.bind(Math.max, null);
用法:
var max = arrayMax([2, 5, 1]);
这可能适合您的目的。
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;
}
comparer
认为在一些特定的范围被称为?因为它引用的this[index]
是undefined
每次。
Math.xxx
)将在全球范围内运行...
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))
这对我有用。
该Math.min
和Math.max
方法都是递归操作正在添加到JS引擎调用堆栈,并且最有可能的崩溃对包含大量项目的数组
(超过〜10⁷项目,取决于用户的浏览器)。
Math.max(... Array(1000000).keys());
未捕获的RangeError:超出最大调用堆栈大小
相反,请使用如下所示的内容:
arr.reduce((max, val) => max > val ? max : val, arr[0])
或具有更好的运行时间:
function maxValue(arr) {
let max = arr[0];
for (let val of arr) {
if (val > max) {
max = val;
}
}
return max;
}
或同时获得最小和最大:
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项测试:
仅供参考,第一个函数运行时(在我的机器上)为15.84ms,而第二个函数运行时仅为4.32ms。
我很惊讶没有人提到减少功能。
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]
我遇到了同样的问题,我需要获取数组的最小值和最大值,而且令我惊讶的是,没有用于数组的内置函数。看了很多书后,我决定自己测试“前三名”解决方案:
测试代码是这样的:
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个随机整数,在具有Windows Vista的intel Pentium 4 2.99GHz台式机上的Mozilla Firefox 28.0上,每个函数执行了10,000次。时间以秒为单位,由performance.now()函数检索。结果是这些,带有3个小数位数和标准偏差:
REDUCE解决方案比离散解决方案慢了117%。APPLY解决方案比离散解决方案更糟,慢了2,118%。此外,正如Peter所言,它不适用于大型数组(大约超过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];
}
计时:平均值= 0.218s,标准偏差= 0.094
因此,它比简单的离散解决方案要慢35%,但是它一次可以检索最大值和最小值(任何其他解决方案至少要检索两倍)。一旦OP需要两个值,离散解决方案将是最佳选择(即使是两个独立的函数,一个用于计算最大值,另一个用于计算最小值,它们也会胜过第二个最佳方案REDUCE解决方案)。
反复进行,随时随地进行跟踪。
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。如果数组中有任何元素,将一遍设置最小值和最大值。
您也可以range
使用上述方法扩展Array,以允许重用并提高可读性。请参阅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);
range
将是同时获得IMO最大值和最小值的最佳方法的函数将是微不足道的-正如我对答案所做的更新所做的那样。
我以为我会分享我简单易懂的解决方案。
对于分钟:
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 arr = [3, 4, 12, 1, 0, 5];
var max = arr[0];
for (var k = 1; k < arr.length; k++) {
if (arr[k] > max) {
max = arr[k];
}
}
console.log("Max is: " + max);
除了使用数学函数max和min之外,另一个要使用的函数是sort()的内置函数:
const nums = [12, 67, 58, 30].sort((x, y) =>
x - y)
let max = nums[0]
let min = nums[nums.length -1]
使用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的解决方案就可以使用。如果没有,请考虑以下问题:
Array.max = function( array ){
return Math.max.apply( Math, array );
};
Array.min = function( array ){
return Math.min.apply( Math, array );
};
如果数组值不是整数,则上面的代码将返回NaN,因此您应该构建一些功能来避免这种情况。否则,它将起作用。
Math
对象用作上下文之外,您的解决方案有何不同?
如果使用库sugar.js,则可以按照您的建议编写arr.min()和arr.max()。您还可以从非数值数组中获取最小值和最大值。
min(map,all = false)返回数组中具有最低值的元素。map可以是映射要检查的值的函数,也可以是用作快捷方式的字符串。如果all为true,则将返回数组中的所有min值。
max(map,all = false)返回数组中具有最大值的元素。map可以是映射要检查的值的函数,也可以是充当快捷方式的字符串。如果all为true,则将返回数组中的所有最大值。
例子:
[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 max= a=> a.reduce((m,x)=> m>x ? m:x);
let min= a=> a.reduce((m,x)=> m<x ? m:x);
对于Math.min / max(+应用),我们得到错误:
超过最大通话堆栈大小(Chrome 74.0.3729.131)
...
带)Math.max()
是这样的:Math.max(...[2, 5, 16, 1])
。请参阅我从MDN文档中得到的答案。