获取数组中出现次数最多的元素

IT技术 javascript mode
2021-01-22 11:14:13

我正在寻找一种优雅的方法来确定JavaScript 数组中哪个元素出现次数最多(mode)。

例如,在

['pear', 'apple', 'orange', 'apple']

'apple'元素是最常见元素。

6个回答

这只是模式。这是一个快速的、非优化的解决方案。它应该是 O(n)。

function mode(array)
{
    if(array.length == 0)
        return null;
    var modeMap = {};
    var maxEl = array[0], maxCount = 1;
    for(var i = 0; i < array.length; i++)
    {
        var el = array[i];
        if(modeMap[el] == null)
            modeMap[el] = 1;
        else
            modeMap[el]++;  
        if(modeMap[el] > maxCount)
        {
            maxEl = el;
            maxCount = modeMap[el];
        }
    }
    return maxEl;
}
这非常有用,如果您可以保证将出现最多次数的单个值。[A,A,B,B,C] 的数组,只返回 A,但这里的模式肯定是 A 和 B?
2021-03-22 11:14:13
很好……但它只适用于字符串——不一定是限制,而是需要考虑的事情。
2021-03-28 11:14:13
我添加了这个算法的一个版本来处理关系。
2021-03-29 11:14:13
我不得不用 if(!modeMap[el]) 替换 `f(modeMap[el] == null) 因为它在传递 [2, 3, 3] 时给我带来了奇怪的数字,因为 modeMap[el] 未定义而不是 null。
2021-03-31 11:14:13
我认为有一个决胜局是合理的,在这种情况下,它是数组中第一个出现的元素。但是你可以很容易地改变这个算法,让你每个人都获得最多的成绩。
2021-03-31 11:14:13

自 2009 年以来,javascript 有了一些发展——我想我会添加另一个选项。我不太关心效率,直到它实际上是一个问题,所以我对“优雅”代码的定义(如 OP 所规定的)有利于可读性——这当然是主观的......

function mode(arr){
    return arr.sort((a,b) =>
          arr.filter(v => v===a).length
        - arr.filter(v => v===b).length
    ).pop();
}

mode(['pear', 'apple', 'orange', 'apple']); // apple

在此特定示例中,如果集合中的两个或多个元素出现相同的次数,则将返回数组中出现最晚的那个。还值得指出的是,它会修改您的原始数组 - 如果您希望Array.slice事先调用,可以防止这种情况发生


编辑:用一些ES6 粗 箭头更新了示例,因为2015 年发生了,我认为它们看起来很漂亮……如果您关心向后兼容性,您可以在修订历史记录中找到它

没问题,但您可以考虑删除您的评论,以免人们看到 +15 并在实际代码库中使用它。再说一次,72 票是主要问题,很难/不可能抵消。
2021-03-20 11:14:13
请注意, arr 将被修改(排序)。建议更改:return [...arr].sort
2021-03-22 11:14:13
这很棒!现在,如果数组中有多个与另一个相同的项目,您将如何返回多个答案?
2021-03-24 11:14:13
@GoranJakovljevic 你能更具体点吗?我想它是ES6 箭头函数- 您是否尝试过修订历史中向后兼容示例
2021-03-29 11:14:13
如果这不是优雅的代码,我不知道是什么。这就像函数式编程的广告。
2021-04-09 11:14:13

根据George Jempty's让算法说明关系的请求,我提出了Matthew Flaschen's算法的修改版本

function modeString(array) {
  if (array.length == 0) return null;

  var modeMap = {},
    maxEl = array[0],
    maxCount = 1;

  for (var i = 0; i < array.length; i++) {
    var el = array[i];

    if (modeMap[el] == null) modeMap[el] = 1;
    else modeMap[el]++;

    if (modeMap[el] > maxCount) {
      maxEl = el;
      maxCount = modeMap[el];
    } else if (modeMap[el] == maxCount) {
      maxEl += "&" + el;
      maxCount = modeMap[el];
    }
  }
  return maxEl;
}

现在将返回一个字符串,其中模式元素由&符号分隔收到结果后,它可以在该&元素上拆分,并且您拥有自己的模式。

另一种选择是返回一组模式元素,如下所示:

function modeArray(array) {
  if (array.length == 0) return null;
  var modeMap = {},
    maxCount = 1,
    modes = [];

  for (var i = 0; i < array.length; i++) {
    var el = array[i];

    if (modeMap[el] == null) modeMap[el] = 1;
    else modeMap[el]++;

    if (modeMap[el] > maxCount) {
      modes = [el];
      maxCount = modeMap[el];
    } else if (modeMap[el] == maxCount) {
      modes.push(el);
      maxCount = modeMap[el];
    }
  }
  return modes;
}

在上面的示例中,您将能够将函数的结果作为模式数组进行处理。

建议更改==to 的实例===以强制执行严格相等
2021-03-11 11:14:13
在第二个例子中(数组一);你不需要设定modes[array[0]]初始值。这将确保您在modes. 这应该可以解决问题var modes = []
2021-03-20 11:14:13
这很棒!但是,当我使用具有两个不同值的数组进行测试时,它会两次返回数组中的第一项。不知道为什么会这样......
2021-03-22 11:14:13
第二个示例的次要细节:如果数组完全由单个项目组成,您将获得相同的数组。如果您希望返回一个空数组,以便您可以告诉您的代码没有元素比其他元素更频繁,请将else if (modeMap[el] == maxCount)条件修改else if (modeMap[el] == maxCount && maxCount > 1)
2021-03-31 11:14:13
@xgrioux 进行 vdlouis 建议的更改以解决此错误。即将 [array[0]] 更改为 []。
2021-04-05 11:14:13

根据Emissary的 ES6+ 答案,您可以Array.prototype.reduce用来进行比较(而不是排序、弹出和可能改变您的数组),我认为这看起来很巧妙。

const mode = (myArray) =>
  myArray.reduce(
    (a,b,i,arr)=>
     (arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
    null)

我默认为 null,如果 null 是您要过滤的可能选项,则它不会总是给您真实的响应,也许这可能是可选的第二个参数

与其他各种解决方案一样,它的缺点是它不处理“绘制状态”,但这仍然可以通过稍微复杂的减少功能来实现。

另一个缺点是,这对于应该是线性运算的东西来说是不必要的二次方。
2021-03-30 11:14:13
a=['pear', 'apple', 'orange', 'apple'];
b={};
max='', maxi=0;
for(let k of a) {
  if(b[k]) b[k]++; else b[k]=1;
  if(maxi < b[k]) { max=k; maxi=b[k] }
}
-1. 4 赞成包含语法错误且不起作用的代码?此代码仅查看属性名称,而不查看值。简洁本身是没有意义的。如果代码失败,则更是如此。
2021-03-13 11:14:13
由于 JavaScript 是传输的,所以看到小的解决方案总是很有趣。
2021-03-30 11:14:13
每次访问 b 至少需要 log(len(b)) 所以 O(n) 可能有点乐观
2021-03-30 11:14:13
这会用全局变量污染窗口,并且不必要地混淆/不可读。没有提供代码如何工作的解释或描述,也没有提供为什么它是一个好的解决方案的动机。
2021-03-30 11:14:13
这仍然是 O(n),但它不必要地使用了两次传递。
2021-04-04 11:14:13