在一个数组中获取出现次数最高的元素

123

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

例如,在以下数组中:

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

'apple'元素是最常见的一个。


你可以从这个Stackoverflow问题中借鉴一些想法。https://dev59.com/eXRA5IYBdhLWcg3wvgsb - Nosredna
我没有仔细阅读解决方案,但它们中是否有任何一种考虑了以下细微差别(优化?),基于仅需确定哪个元素具有最多出现次数的要求,而不是最多出现次数是多少...当循环数组时,如果计数可以停止,则差异在最高和次高出现次数之间小于剩余要循环的元素数量,循环可以停止,当前最高值将是最高值。 - Dexygen
这是一个与编程语言无关的问题,位于算法-在大型单词序列中查找前K个频繁单词的最有效方法-堆栈溢出 - user202729
44个回答

3

又到了寻找另一个解决方案的时候了:

function getMaxOccurrence(arr) {
    var o = {}, maxCount = 0, maxValue, m;
    for (var i=0, iLen=arr.length; i<iLen; i++) {
        m = arr[i];

        if (!o.hasOwnProperty(m)) {
            o[m] = 0;
        }
        ++o[m];

        if (o[m] > maxCount) {
            maxCount = o[m];
            maxValue = m;
        }
    }
    return maxValue;
}

如果简洁很重要(其实不是),那么:

function getMaxOccurrence(a) {
    var o = {}, mC = 0, mV, m;
    for (var i=0, iL=a.length; i<iL; i++) {
        m = a[i];
        o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
        if (o[m] > mC) mC = o[m], mV = m;
    }
    return mV;
}

如果要避免不存在的成员(例如稀疏数组),需要进行额外的hasOwnProperty测试:

function getMaxOccurrence(a) {
    var o = {}, mC = 0, mV, m;
    for (var i=0, iL=a.length; i<iL; i++) {
        if (a.hasOwnProperty(i)) {
            m = a[i];
            o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
            if (o[m] > mC) mC = o[m], mV = m;
        }
    }
    return mV;
}

getMaxOccurrence([,,,,,1,1]); // 1

其他答案会返回未定义

2
@Jonah,仅为简洁而简洁是毫无意义的,通常会使代码更难阅读和维护。当然,更冗长的代码并不一定比更短的代码更好。但是,这些标准本身已被更重要的措施所取代,例如清晰度和可维护性。 - RobG
1
显然,晦涩难懂的简洁从来不是目标。但一般来说,如果两个版本的代码密度大致相等,较短的代码通常更清晰、更好。我并不是说这是一个“规则”,但相关性很强。事实上,我认为没有其他单一指标与可读性的相关性如此之高。这就是为什么每个程序员都喜欢删除代码的原因。这也是为什么 Code Review 中的大多数重写都比原始代码更短的原因。 - Jonah

3
这是另一种使用ES6的方式,具有O(n)复杂度。
const result = Object.entries(
    ['pear', 'apple', 'orange', 'apple'].reduce((previous, current) => {
        if (previous[current] === undefined) previous[current] = 1;
        else previous[current]++;
        return previous;
    }, {})).reduce((previous, current) => (current[1] >= previous[1] ? current : previous))[0];
console.log("Max value : " + result);

如果有重复项,此代码不会捕获重复项(例如尝试运行['pear','apple','orange','orange','apple'])。 - maxshuty

2
function mode(arr){
  return arr.reduce(function(counts,key){
    var curCount = (counts[key+''] || 0) + 1;
    counts[key+''] = curCount;
    if (curCount > counts.max) { counts.max = curCount; counts.mode = key; }
    return counts;
  }, {max:0, mode: null}).mode
}

这个解决方案的问题在于,单词“max”和“mode”不会被计算为它们是映射逻辑的一部分。 - Pablo

2
    const frequence = (array) =>
      array.reduce(
        (acc, item) =>
          array.filter((v) => v === acc).length >=
          array.filter((v) => v === item).length
            ? acc
            : item,
        null
      );

frequence([1, 1, 2])

2
// O(n)
var arr = [1, 2, 3, 2, 3, 3, 5, 6];
var duplicates = {};
max = '';
maxi = 0;
arr.forEach((el) => {
    duplicates[el] = duplicates[el] + 1 || 1;
  if (maxi < duplicates[el]) {
    max = el;
    maxi = duplicates[el];
  }
});
console.log(max);

2
这个解决方案可以在元素得分相同时返回数组的多个元素。例如,一个数组:
arr = [ 3, 4, 3, 6, 4, ];

有两种模式值:36

以下是解决方案。

function find_mode(arr) {
    var max = 0;
    var maxarr = [];
    var counter = [];
    var maxarr = [];

    arr.forEach(function(){
       counter.push(0);
    });

    for(var i = 0;i<arr.length;i++){
       for(var j=0;j<arr.length;j++){
            if(arr[i]==arr[j])counter[i]++; 
       }
    } 


    max=this.arrayMax(counter);   
  
    for(var i = 0;i<arr.length;i++){
         if(counter[i]==max)maxarr.push(arr[i]);
    }

    var unique = maxarr.filter( this.onlyUnique );
    return unique;

  };


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

 function onlyUnique(value, index, self) {
       return self.indexOf(value) === index;
 }

2
另一个 JavaScript 解决方案来自:https://www.w3resource.com/javascript-exercises/javascript-array-exercise-8.php 也可以尝试这个:
let arr =['pear', 'apple', 'orange', 'apple'];

function findMostFrequent(arr) {
  let mf = 1;
  let m = 0;
  let item;

  for (let i = 0; i < arr.length; i++) {
    for (let j = i; j < arr.length; j++) {
      if (arr[i] == arr[j]) {
        m++;
        if (m > mf) {
          mf = m;
          item = arr[i];
        }
      }
    }
    m = 0;
  }

  return item;
}

findMostFrequent(arr); // apple

1
var array = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17],
    c = {}, // counters
    s = []; // sortable array

for (var i=0; i<array.length; i++) {
    c[array[i]] = c[array[i]] || 0; // initialize
    c[array[i]]++;
} // count occurrences

for (var key in c) {
    s.push([key, c[key]])
} // build sortable array from counters

s.sort(function(a, b) {return b[1]-a[1];});

var firstMode = s[0][0];
console.log(firstMode);

1

也可以尝试一下,这不考虑浏览器版本。

function mode(arr){
var a = [],b = 0,occurrence;
    for(var i = 0; i < arr.length;i++){
    if(a[arr[i]] != undefined){
        a[arr[i]]++;
    }else{
        a[arr[i]] = 1;
    }
    }
    for(var key in a){
    if(a[key] > b){
        b = a[key];
        occurrence = key;
    }
    }
return occurrence;
}
alert(mode(['segunda','terça','terca','segunda','terça','segunda']));

请注意,当两个或更多条目出现相同次数时,此函数返回数组中最新的发生!

1
这是我的解决方案,使用数字和新的“Set”功能。它的性能不是很好,但我写它时确实很开心,并且支持多个最大值。
const mode = (arr) => [...new Set(arr)]
  .map((value) => [value, arr.filter((v) => v === value).length])
  .sort((a,b) => a[1]-b[1])
  .reverse()
  .filter((value, i, a) => a.indexOf(value) === i)
  .filter((v, i, a) => v[1] === a[0][1])
  .map((v) => v[0])

mode([1,2,3,3]) // [3]
mode([1,1,1,1,2,2,2,2,3,3,3]) // [1,2]

顺便说一下,不要在生产环境中使用此方法,这只是一个演示如何仅使用ES6和数组函数解决它的示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接