列表中最常出现的数字(模式) - 只想获得最高值

时间:2016-10-08 21:04:48

标签: javascript mode

我正在尝试获取数组中最常出现的数字,因此对于包含1,2,10,5,1的数组,结果应为1.我写的代码返回的频率为每个数字,所以1发生两次,2发生一次,10发生一次等任何建议如何我可以修复我的结果?

function mode(arr) {
var uniqNum = {};
var numCounter = function(num, counter) {
  if(!uniqNum.hasOwnProperty(num)) {
    uniqNum[num] = 1;
  } else {
    uniqNum[num] ++;
    }
};
arr.forEach(numCounter);
return uniqNum;
}

4 个答案:

答案 0 :(得分:1)

首先,我们要创建一个数组,用于计算特定值到此时的出现次数

然后我们使用reduce函数返回从原始数组中读取的值的数组,以获取其值具有当前最大外观的索引。我们重新定义最大值并清空模式的最终输出数组(如果建立了新的最大值)。我们希望这是一个集合,以防万一出现最大限度的外观。

以下的其他优点是它不需要更昂贵的排序o(nlog n)并且将时间复杂度保持为线性。我还希望将所使用的功能仅保留为两个(map和reduce),因为在这种情况下只需要它。

编辑:修复了一个主要错误uniqNum [e] + = 1而不是uniqNum [e] + 1因为我的初始案例数组仍然返回预期结果而被忽视。还使语法更简洁,有利于更多评论。



var arr = [1,2,10,5,1,5,2,2,5,3,3];
//global max to keep track of which value has most appearances.
var max = -1;
var uniqNum = {};

     var modeArray = arr.map(function(e) {
     //create array that counts appearances of the value up to that point starting from beginning of the input arr array.       
      if(!uniqNum.hasOwnProperty(e)) {
              uniqNum[e] = 1;
              return 1;
         } else {
              return uniqNum[e] += 1;
          }
        //reduce the above appearance count array into an array that only contains values of the modes
       }).reduce(function (modes, e1, i) {
              //if max gets beaten then redefine the mode array to only include the new max appearance value.
              if(e1 > max){
                  //redefining max
                  max = e1;
                  //returning only the new max element
                  return [arr[i]];
                  //if its a tie we still want to include the current value but we don't want to empty the array.
                }else if(e1 == max){
                   //append onto the modes array the co-max value
                   return[...modes, arr[i]];
                }
                return modes;
        },[]);

alert(modeArray);




这是一个测试,你可以运行我对@acontell的解决方案。在我的浏览器(Chrome with V8)中,我的解决方案对于具有大量重复值的阵列来说快了大约四到四倍,对于具有较少重复值的分布来说甚至更大的优势。 @acontell' s确实是一个更清洁的解决方案,但绝对不会更快执行。



    var arr = [];
    for(var i=0; i < 100000; i++){
            arr.push(Math.floor(Math.random() * (100 - 1)) + 1);
        
    }
    
    console.time("test"); 
    test();



    function test(){

    var max = -1;
    var uniqNum = {};

         var modeArray = arr.map(function(e) {
         //create array that counts appearances of the value up to that point starting from beginning of the input arr array.       
          if(!uniqNum.hasOwnProperty(e)) {
                  uniqNum[e] = 1;
                  return 1;
             } else {
                  return uniqNum[e] += 1;
              }
            //reduce the above appearance count array into an array that only contains values of the modes
           }).reduce(function (modes, e1, i) {
                  //if max gets beaten then redefine the mode array to only include the new max appearance value.
                  if(e1 > max){
                      //redefining max
                      max = e1;
                      //returning only the new max element
                      return [arr[i]];
                      //if its a tie we still want to include the current value but we don't want to empty the array.
                    }else if(e1 == max){
                       //append onto the modes array the co-max value
                        modes.push(arr[i])    
                       return modes;
                    }
                    return modes;
            },[]);


    }

    console.timeEnd("test");

console.time("test1");
test1();

function test1 () {
  var freq = [],
        uniqNum = {},
        i;
      arr.forEach(function(num) {
        uniqNum[num] = i = (uniqNum[num] || 0) + 1;
        freq[i] = (freq[i] || []).concat(num);
      });
      return freq[freq.length - 1];

}

console.timeEnd("test1");
&#13;
&#13;
&#13;

答案 1 :(得分:1)

我保持你的代码不变,并添加了一些额外的声明。以下是演示:http://codepen.io/PiotrBerebecki/pen/rrdxRo

function mode(arr) {
  var uniqNum = {};

  var numCounter = function(num, counter) {
    if(!uniqNum.hasOwnProperty(num)) {
      uniqNum[num] = 1;
    } else {
      uniqNum[num] ++;
    }
  };

  arr.forEach(numCounter);

  return Object.keys(uniqNum)
    .sort((a,b) => uniqNum[b] - uniqNum[a])                       // sort by frequency
    .filter((val,ind,array) => uniqNum[array[0]] == uniqNum[val]) // leave only most frequent
    .map(val => Number(val));                                     // convert text to number
}

console.log(  JSON.stringify(mode([3,3,2,4,4]))  ) // [3,4]
console.log(  JSON.stringify(mode([2,4,3,3]))    ) // [3]

答案 2 :(得分:1)

我认为只有对forEach循环进行一点修改并辅助其他辅助数据结构才能完成:

function mode(arr) {
    var freq = [], uniqNum = {}, i;
    arr.forEach(function (num) {
        uniqNum[num] = i = (uniqNum[num] || 0) + 1;
        freq[i] = (freq[i] || []).concat(num);
    });
    return freq[freq.length - 1];
}

console.log(mode([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 6, 7, 1, 6]));

只需对数组的所有元素进行一次迭代,我们就可以收集足够的信息来打印出结果:

  1. uniqNum是您创建的用于收集有关元素频率的信息的集合。
  2. freq将是一个数组,其中最后一个元素将包含一个具有更高频率元素的数组。
  3. Fiddle。希望它有所帮助。

答案 3 :(得分:0)

我尝试过使用原生js函数来解决这个问题。

var arr = [1,2,10,5,1];

// groupBy number
var x = arr.reduce(
    function(ac, cur){ 
         ac[cur]?(ac[cur] = ac[cur] + 1):ac[cur] = 1; 
         return ac;
     }, {}
);

// sort in order of frequencies
var res = Object.keys(x).sort(
    function(a,b){ return x[a] < x[b]}
);

res[0] has the most frequent element