计算数组元素的出现次数/频率

| 在Javascript中,我试图获取数字值的初始数组并计算其中的元素。理想情况下,结果将是两个新数组,第一个数组指定每个唯一元素,第二个数组包含每个元素出现的次数。但是,我愿意就输出格式提出建议。 例如,如果初始数组为:
5, 5, 5, 2, 2, 2, 2, 2, 9, 4
然后将创建两个新的数组。第一个将包含每个唯一元素的名称:
5, 2, 9, 4
第二个将包含元素在初始数组中出现的次数:
3, 5, 1, 1
因为数字5在初始数组中出现3次,所以数字2出现5次,而9和4都出现一次。 我已经寻找了很多解决方案,但似乎没有任何效果,而且我尝试过的所有事情都变得异常复杂。任何帮助,将不胜感激! 谢谢 :)     
已邀请:
干得好:
function foo(arr) {
    var a = [], b = [], prev;

    arr.sort();
    for ( var i = 0; i < arr.length; i++ ) {
        if ( arr[i] !== prev ) {
            a.push(arr[i]);
            b.push(1);
        } else {
            b[b.length-1]++;
        }
        prev = arr[i];
    }

    return [a, b];
}
现场演示:http://jsfiddle.net/simevidas/bnACW/   注意      这将使用
Array.sort
更改原始输入数组的顺序。     
您可以使用一个对象保存结果:
var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var counts = {};

for (var i = 0; i < arr.length; i++) {
  var num = arr[i];
  counts[num] = counts[num] ? counts[num] + 1 : 1;
}

console.log(counts[5], counts[2], counts[9], counts[4]);
var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].reduce(function (acc, curr) {
  if (typeof acc[curr] == \'undefined\') {
    acc[curr] = 1;
  } else {
    acc[curr] += 1;
  }

  return acc;
}, {});

// a == {2: 5, 4: 1, 5: 3, 9: 1}
    
如果使用下划线或破折号,这是最简单的操作:
_.countBy(array);
这样:
_.countBy([5, 5, 5, 2, 2, 2, 2, 2, 9, 4])
=> Object {2: 5, 4: 1, 5: 3, 9: 1}
正如其他人指出的那样,您可以随后对结果执行
_.keys()
_.values()
函数,以分别获取唯一编号及其出现的位置。但是以我的经验,原始对象要容易得多。     
不要为结果使用两个数组,请使用一个对象:
a      = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
result = { };
for(var i = 0; i < a.length; ++i) {
    if(!result[a[i]])
        result[a[i]] = 0;
    ++result[a[i]];
}
result
看起来像:
{
    2: 5,
    4: 1,
    5: 3,
    9: 1
}
    
ECMAScript2015选项如何。
const a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

const aCount = new Map([...new Set(a)].map(
    x => [x, a.filter(y => y === x).length]
));
aCount.get(5)  // 3
aCount.get(2)  // 5
aCount.get(9)  // 1
aCount.get(4)  // 1
本示例将输入数组传递给
Set
构造函数,以创建唯一值的集合。然后,spread语法会将这些值扩展为一个新数组,因此我们可以调用
map
并将其转换为
[value, count]
对的二维数组-即以下结构:
Array [
   [5, 3],
   [2, 5],
   [9, 1],
   [4, 1]
]
然后将新数组传递给
Map
构造函数,从而产生一个可迭代的对象:
Map {
    5 => 3,
    2 => 5,
    9 => 1,
    4 => 1
}
Map
对象的妙处在于它保留了数据类型-也就是说
aCount.get(5)
将返回
3
,而
aCount.get(\"5\")
将返回
undefined
。它还允许将任何值/类型用作键,这意味着该解决方案也将与对象数组一起使用。
function frequencies(/* {Array} */ a){
    return new Map([...new Set(a)].map(
        x => [x, a.filter(y => y === x).length]
    ));
}

let foo = { value: \'foo\' },
    bar = { value: \'bar\' },
    baz = { value: \'baz\' };

let aNumbers = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
    aObjects = [foo, bar, foo, foo, baz, bar];

frequencies(aNumbers).forEach((val, key) => console.log(key + \': \' + val));
frequencies(aObjects).forEach((val, key) => console.log(key.value + \': \' + val));
我认为这是最简单的方法来计算数组中具有相同值的出现次数。
var a = [true, false, false, false];
a.filter(function(value){
    return value === false;
}).length
    
const data = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

function count(arr) {
  return arr.reduce((prev, curr) => (prev[curr] = ++prev[curr] || 1, prev), {})
}

console.log(count(data))
如果您偏爱单一衬板。
arr.reduce(function(countMap, word) {countMap[word] = ++countMap[word] || 1;return countMap}, {});
编辑(6/12/2015): 由内而外的解释。 countMap是一个映射图,它用单词的频率映射单词,我们可以看到匿名函数。 reduce所做的是将带有参数的函数应用于所有数组元素,并将countMap作为最后一个函数调用的返回值传递。最后一个参数({})是第一个函数调用的countMap的默认值。     
ES6版本应该简化很多(另一行解决方案)
let arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
let acc = arr.reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());

console.log(acc);
// output: Map { 5 => 3, 2 => 5, 9 => 1, 4 => 1 }
一个Map而不是Plain Object可以帮助我们区分不同类型的元素,否则所有计数都基于字符串     
一线ES6解决方案。使用对象作为地图的答案如此之多,但我看不到有人使用实际的地图
const map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
使用
map.keys()
获得独特的元素 用
map.values()
得到 用
map.entries()
获得对[元素,频率] 我参加聚会有点晚了,但我希望至少有人会有所帮助。
var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

const map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());

console.info([...map.keys()])
console.info([...map.values()])
console.info([...map.entries()])
如果您使用下划线,则可以使用功能路线
a = [\'foo\', \'foo\', \'bar\'];

var results = _.reduce(a,function(counts,key){ counts[key]++; return counts },
                  _.object( _.map( _.uniq(a), function(key) { return [key, 0] })))
所以你的第一个数组是
_.keys(results)
第二个数组是
_.values(results)
如果可用,其中大多数将默认为本地javascript函数 演示:http://jsfiddle.net/dAaUU/     
根据@adamse和@pmandell(我赞成)的答案,在ES6中,您可以在一行中完成此操作: 2017 edit:我使用
||
来减少代码大小并提高可读性。
var a=[7,1,7,2,2,7,3,3,3,7,,7,7,7];
alert(JSON.stringify(

a.reduce((r,k)=>{r[k]=1+r[k]||1;return r},{})

));
您可以扩展Array原型,如下所示:
Array.prototype.frequencies = function() {
    var l = this.length, result = {all:[]};
    while (l--){
       result[this[l]] = result[this[l]] ? ++result[this[l]] : 1;
    }
    // all pairs (label, frequencies) to an array of arrays(2)
    for (var l in result){
       if (result.hasOwnProperty(l) && l !== \'all\'){
          result.all.push([ l,result[l] ]);
       }
    }
    return result;
};

var freqs = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].frequencies();
alert(freqs[2]); //=> 5
// or
var freqs = \'1,1,2,one,one,2,2,22,three,four,five,three,three,five\'
             .split(\',\')
             .frequencies();
alert(freqs.three); //=> 3
或者,您可以使用
Array.map
  Array.prototype.frequencies  = function () {
    var freqs = {sum: 0}; 
    this.map( function (a){ 
        if (!(a in this)) { this[a] = 1; } 
        else { this[a] += 1; }
        this.sum += 1;
        return a; }, freqs
    );
    return freqs;
  }
    
这只是眼睛轻而易用的东西...
function count(a,i){
 var result = 0;
 for(var o in a)
  if(a[o] == i)
   result++;
 return result;
}
编辑:既然你想要所有的事情...
function count(a){
 var result = {};
 for(var i in a){
  if(result[a[i]] == undefined) result[a[i]] = 0;
  result[a[i]]++;
 }
 return result;
}
    
因此,这就是我将如何使用一些最新的javascript功能来做到这一点: 首先,将数组减少到计数的20%:
let countMap = array.reduce(
  (map, value) => {map.set(value, (map.get(value) || 0) + 1); return map}, 
  new Map()
)
通过使用
Map
,起始数组可以包含任何类型的对象,并且计数将是正确的。没有
Map
,某些类型的对象将给您带来奇怪的数。 有关差异的更多信息,请参见ѭ20文档。 如果您所有的值都是符号,数字或字符串,也可以用一个对象来完成:
let countObject = array.reduce(
  (map, value) => { map[value] = (map[value] || 0) + 1; return map },
  {}
)
或者使用解构和对象散布语法以某种功能性稍稍稍稍变而无变化:
let countObject = array.reduce(
  (value, {[value]: count = 0, ...rest}) => ({ [value]: count + 1, ...rest }),
  {}
)
此时,您可以使用
Map
或对象进行计数(与对象不同,地图可以直接迭代),或将其转换为两个数组。 对于
Map
countMap.forEach((count, value) => console.log(`value: ${value}, count: ${count}`)

let values = countMap.keys()
let counts = countMap.values()
或针对对象:
Object
  .entries(countObject) // convert to array of [key, valueAtKey] pairs
  .forEach(([value, count]) => console.log(`value: ${value}, count: ${count}`)

let values = Object.keys(countObject)
let counts = Object.values(countObject)
    
var array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

function countDuplicates(obj, num){
  obj[num] = (++obj[num] || 1);
  return obj;
}

var answer = array.reduce(countDuplicates, {});
// answer => {2:5, 4:1, 5:3, 9:1};
  如果仍然需要两个数组,则可以使用像这样的答案...
var uniqueNums = Object.keys(answer);
// uniqueNums => [\"2\", \"4\", \"5\", \"9\"];

var countOfNums = Object.keys(answer).map(key => answer[key]);
// countOfNums => [5, 1, 3, 1];
  或者如果您希望uniqueNums为数字
var uniqueNums = Object.keys(answer).map(key => +key);
// uniqueNums => [2, 4, 5, 9];
    
具有reduce(固定)的ES6解决方案:
const arr = [2, 2, 2, 3, 2]

const count = arr.reduce((pre, cur) => (cur === 2) ? ++pre : pre, 0)
console.log(count) // 4
使用Set可以提取唯一数字,然后可以过滤输入以获得每个数字的计数:
input = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
setOcc = new Set(input)
arrOcc = [...setOcc]
arrNumberOcc = arrOcc.map(occ => input.filter(e => e===occ).length);
console.log(arrOcc, arrNumberOcc)
我的ramda解决方案:
const testArray = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]

const counfFrequency = R.compose(
  R.map(R.length),
  R.groupBy(R.identity),
)

counfFrequency(testArray)
链接到REPL。     
查看下面的代码。
<html>
<head>
<script>
// array with values
var ar = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

var Unique = []; // we\'ll store a list of unique values in here
var Counts = []; // we\'ll store the number of occurances in here

for(var i in ar)
{
    var Index = ar[i];
    Unique[Index] = ar[i];
    if(typeof(Counts[Index])==\'undefined\')  
        Counts[Index]=1;
    else
        Counts[Index]++;
}

// remove empty items
Unique = Unique.filter(function(){ return true});
Counts = Counts.filter(function(){ return true});

alert(ar.join(\',\'));
alert(Unique.join(\',\'));
alert(Counts.join(\',\'));

var a=[];

for(var i=0; i<Unique.length; i++)
{
    a.push(Unique[i] + \':\' + Counts[i] + \'x\');
}
alert(a.join(\', \'));

</script>
</head>
<body>

</body>
</html>
    
尝试这个:
Array.prototype.getItemCount = function(item) {
    var counts = {};
    for(var i = 0; i< this.length; i++) {
        var num = this[i];
        counts[num] = counts[num] ? counts[num]+1 : 1;
    }
    return counts[item] || 0;
}
    
使用MAP,您可以在输出中包含2个数组:一个包含事件的数组,另一个包含事件的数量。
const dataset = [2,2,4,2,6,4,7,8,5,6,7,10,10,10,15];
let values = [];
let keys = [];

var mapWithOccurences = dataset.reduce((a,c) => {
  if(a.has(c)) a.set(c,a.get(c)+1);
  else a.set(c,1);
  return a;
}, new Map())
.forEach((value, key, map) => {
  keys.push(key);
  values.push(value);
});


console.log(keys)
console.log(values)
这是最简单的解决方案
const data = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
let occurance_arr=[]; 
const aCount =  [...new Set(data)].map(x => {
   occurance_arr.push(data.filter(y=> y==x).length)
});
console.log(occurance_arr);   //[3, 5, 1, 1]
    
这个问题已有8年以上的历史了,很多答案并没有真正考虑到ES6及其众多优点。 每当我们创建其他数组,制作数组的两倍或三倍副本,甚至将数组转换为对象时,考虑我们的代码对垃圾回收/内存管理的后果也许更为重要。这些对于小型应用程序来说是微不足道的观察,但是如果规模是一个长期目标,那么请仔细考虑这些。 如果您只需要特定数据类型的\“ counter \”并且起始点是一个数组(我想您因此需要一个有序列表,并利用数组提供的许多属性和方法),则可以简单地遍历array1并使用在array1中找到的值和这些值的出现次数填充array2。 就如此容易。 用于面向对象编程和面向对象设计的简单类SimpleCounter(ES6)的示例
class SimpleCounter { 

    constructor(rawList){ // input array type
        this.rawList = rawList;
        this.finalList = [];
    }

    mapValues(){ // returns a new array

        this.rawList.forEach(value => {
            this.finalList[value] ? this.finalList[value]++ : this.finalList[value] = 1;
        });

        this.rawList = null; // remove array1 for garbage collection

        return this.finalList;

    }

}

module.exports = SimpleCounter;
    
我在代码战中解决了类似的问题,并设计了以下对我有用的解决方案。 这给出了数组中整数的最高计数,也给出了整数本身。我认为它也可以应用于字符串数组。 为了正确地对琴弦进行分类,请从
sort()
部分的内侧取出
function(a, b){return a-b}
function mostFrequentItemCount(collection) {
    collection.sort(function(a, b){return a-b});
    var i=0;
    var ans=[];
    var int_ans=[];
    while(i<collection.length)
    {
        if(collection[i]===collection[i+1])
        {
            int_ans.push(collection[i]);
        }
        else
        {
            int_ans.push(collection[i]);
            ans.push(int_ans);
            int_ans=[];
        }
        i++;
    }

    var high_count=0;
    var high_ans;

    i=0;
    while(i<ans.length)
    {
        if(ans[i].length>high_count)
        {
            high_count=ans[i].length;
            high_ans=ans[i][0];
        }
        i++;
    }
    return high_ans;
}
    
有一种更好更好的方法,我们可以使用
ramda.js
进行此操作。 这里的代码示例
const ary = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
R.countBy(r=> r)(ary)
countBy文档在文档中     
关于我的评论,请@Emissary调整其解决方案。即时通讯增加了我处理的方式:
let distinctArr = yourArray.filter((curElement, index, array) => array.findIndex(t =>    t.prop1=== curElement.prop1 && t.prop2 === curElement.prop2 && t.prop3=== curElement.prop3) === index);
let distinctWithCount = [...new Set(distinctArr)].map(function(element){element.prop4 = yourArray.filter(t =>    t.prop1=== element.prop1 && t.prop2 === element.prop2 && t.prop2=== element.prop2).length;
我在这里做的是,首先删除重复项并保存数组(distinctArr),然后在原始数组(yourArray)上计算对象被复制的时间,然后在出现的值上添加第4个属性 希望它对需要此特定解决方案的人有所帮助 Ofc由ES6制成     
这是一种计算对象数组中出现次数的方法。它还将第一个数组的内容放置在新数组中以对值进行排序,从而不会破坏原始数组中的顺序。然后使用递归函数遍历每个元素并计算数组内每个对象的数量属性。
var big_array = [
  { name: \"Pineapples\", quantity: 3 },
  { name: \"Pineapples\", quantity: 1 },
  { name: \"Bananas\", quantity: 1 },
  { name: \"Limes\", quantity: 1 },
  { name: \"Bananas\", quantity: 1 },
  { name: \"Pineapples\", quantity: 2 },
  { name: \"Pineapples\", quantity: 1 },
  { name: \"Bananas\", quantity: 1 },
  { name: \"Bananas\", quantity: 1 },
  { name: \"Bananas\", quantity: 5 },
  { name: \"Coconuts\", quantity: 1 },
  { name: \"Lemons\", quantity: 2 },
  { name: \"Oranges\", quantity: 1 },
  { name: \"Lemons\", quantity: 1 },
  { name: \"Limes\", quantity: 1 },
  { name: \"Grapefruit\", quantity: 1 },
  { name: \"Coconuts\", quantity: 5 },
  { name: \"Oranges\", quantity: 6 }
];

function countThem() {
  var names_array = [];
  for (var i = 0; i < big_array.length; i++) {
    names_array.push( Object.assign({}, big_array[i]) );
  }

  function outerHolder(item_array) {
    if (item_array.length > 0) {
      var occurrences = [];
      var counter = 0;
      var bgarlen = item_array.length;
      item_array.sort(function(a, b) { return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0); });

      function recursiveCounter() {
        occurrences.push(item_array[0]);
        item_array.splice(0, 1);
        var last_occurrence_element = occurrences.length - 1;
        var last_occurrence_entry = occurrences[last_occurrence_element].name;
        var occur_counter = 0;
        var quantity_counter = 0;
        for (var i = 0; i < occurrences.length; i++) {
          if (occurrences[i].name === last_occurrence_entry) {
            occur_counter = occur_counter + 1;
            if (occur_counter === 1) {
              quantity_counter = occurrences[i].quantity;
            } else {
              quantity_counter = quantity_counter + occurrences[i].quantity;
            }
          }
        }

        if (occur_counter > 1) {
          var current_match = occurrences.length - 2;
          occurrences[current_match].quantity = quantity_counter;
          occurrences.splice(last_occurrence_element, 1);
        }

        counter = counter + 1;

        if (counter < bgarlen) {
          recursiveCounter();
        }
      }

      recursiveCounter();

      return occurrences;
    }
  }
  alert(JSON.stringify(outerHolder(names_array)));
}
    
function countOcurrences(arr){
    return arr.reduce((aggregator, value, index, array) => {
      if(!aggregator[value]){
        return aggregator = {...aggregator, [value]: 1};  
      }else{
        return aggregator = {...aggregator, [value]:++aggregator[value]};
      }
    }, {})
}
    

要回复问题请先登录注册