在 JavaScript 中查找数组的最小/最大元素

IT技术 javascript
2021-01-26 22:59:08

如何轻松获取 JavaScript 数组的最小或最大元素?

示例伪代码:

let array = [100, 0, 50]

array.min() //=> 0
array.max() //=> 100
6个回答

如何增加内置 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 );
只是分享我在上面的代码中犯的一个 jQuery 错误,它花了我很长时间来调试。jquery 数组适用于除 iPad 以外的所有设备。我必须将数组转换为真正的本机数组才能工作。由于某种原因只影响了单个设备Math.max.apply(null, $.makeArray(array));
2021-03-15 22:59:08
@HankH:通过nullMath{}或任何对结果apply()call()与结果无关。Math.max不也不应该在this内部引用
2021-03-20 22:59:08
我投了反对票,因为提议的方法在堆栈帧中消耗 O(n) 内存,因此在大型阵列上崩溃。就我而言,大约 130000 个数字足以使 nodejs 崩溃。
2021-03-28 22:59:08
不要像这样增加内置原型。这不仅仅是与其他库的冲突;它还与浏览器本身在未来提供.maxor.min方法的潜力有关完全现实的场景:您使用此答案。2016 年,ES7 或 ES8 规范Array.maxArray.min. 与此版本不同,它们处理字符串。您未来的同事尝试使用现已记录良好的本机.max()方法获取数组中按字母顺序排列的最新字符串,但神秘地获取了NaN. 几个小时后,她找到了这个代码,运行了一个git blame,然后诅咒你的名字。
2021-03-28 22:59:08
作为 C# 程序员,我需要强类型问题。
2021-04-02 22:59:08
var max_of_array = Math.max.apply(Math, array);

有关完整讨论,请参阅:http : //aaroncrane.co.uk/2008/11/javascript_max_api/

@ziyuang 当你像 一样调用它时Math.max(a,b)它会Math作为this传递,所以在用 调用时做同样的事情可能是有意义的applyMath.max不使用该this值,因此您可以传递任何您想要的值。
2021-03-09 22:59:08
Math.max.apply(Math, array)和 和有什么不一样Math.max.apply(null, array)博客上说“...你也必须重复说max属于Math...”,但似乎我不必这样做(通过设置applyas的第一个参数null)。
2021-03-12 22:59:08

对于大阵列(〜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)) ;)
2021-03-11 22:59:08
jsperf.com/array-min-max-random/1 从 60 个元素开始 如果数组大小大于 60,则 Math 方法与 while 循环不相等,则 Math 方法获胜。数组越大 - 数学方法越多。(对于 100 elems Math.min/max 快 10%,对于 1000 elems,它的 +25% )
2021-03-22 22:59:08
@Venugopal 然后您需要进行特殊检查以查看数组是否为空并返回 +/- Infinity
2021-03-29 22:59:08
2019 年reduce解决方案是最慢的。即使您使用具有数百万个元素的数组,最好使用标准 for loop更多内容请看我的回答。
2021-04-03 22:59:08
奇怪......我去了链接的网站......并在Firefox 51.0.0 / Mac OS X 10.12.0中进行测试,基于reduce的方法比基于循环的方法慢30%......结果非常不同
2021-04-07 22:59:08

使用扩展运算符 (ES6)

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

是的,我的意思是 javascript 规范很糟糕。显然无法计算没有数字的最小值。在其他更严重的编程语言中,例如 Scala,要求空数组的最小值会引发异常。
2021-03-11 22:59:08
此解决方案已由多个 其他答案提供。
2021-03-14 22:59:08
@DavidPortabella 不知道为什么这很有趣。这就是它根据规范工作的方式If no arguments are given, the result is -∞.
2021-04-03 22:59:08
Scala 适用于需要机器告诉他们他们做错了的人
2021-04-04 22:59:08
Math.max(...[]) = -Infinity。哈哈哈😂😂😂
2021-04-05 22:59:08

tl;博士

// 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);

数组的最大大小

根据MDNapply和扩散的解决方案必须从参数的最大数目的限制来了65536的限制:

但请注意:以这种方式使用 apply 时,您可能会面临超出 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是最慢的。

几乎所有的测试都给出了相同的结果,除了一个最终传播速度最慢的测试。

如果您将数组增加到 100 万个项目,事情就会开始破裂,您将使用标准循环作为快速解决方案和reduce较慢的解决方案

JSPerf 基准测试

jsperf.com 不同解决方案的基准测试结果,用于查找数组的最小/最大项

JSBen 基准测试

jsben.com 用于查找数组最小/最大项的不同解决方案的基准测试结果

JSBench.me 基准测试

jsbench.me 不同解决方案的基准测试结果,用于查找数组的最小/最大项

基准源代码

@SlavaFominII 我扩展了答案,因此它涵盖了具有数千个元素的数组。
2021-03-28 22:59:08
如果您使用的是typescript,则如图所示的展开运算符被编译Math.max.apply(Math, arr)为“最大”兼容性。
2021-03-29 22:59:08
同样来自 MDN:“如果数组有太多元素,两者都会传播(...)并且apply要么失败要么返回错误的结果[...]reduce 解决方案没有这个问题”测试 Chrome、FF、Edge 和 IE11 似乎是对于最多 10 万个值的数组,可以。(在 Win10 和最新浏览器上测试:Chrome 110k、Firefox 300k、Edge 400k、IE11 150k)。
2021-04-05 22:59:08
这是一个非常慢的方法,如果数组有数千个元素怎么办?
2021-04-07 22:59:08