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

Jac*_*k W 195 javascript arrays element count

在Javascript中,我试图获取一个初始数值值数组并计算其中的元素.理想情况下,结果将是两个新数组,第一个指定每个唯一元素,第二个包含每个元素出现的次数.但是,我愿意接受有关输出格式的建议.

例如,如果初始数组是:

5, 5, 5, 2, 2, 2, 2, 2, 9, 4
Run Code Online (Sandbox Code Playgroud)

然后将创建两个新阵列.第一个将包含每个唯一元素的名称:

5, 2, 9, 4
Run Code Online (Sandbox Code Playgroud)

第二个将包含元素在初始数组中出现的次数:

3, 5, 1, 1
Run Code Online (Sandbox Code Playgroud)

因为数字5在初始数组中出现三次,所以数字2出现五次,9和4出现一次.

我经常搜索一个解决方案,但似乎没有任何工作,我自己尝试过的所有东西都变得荒谬复杂.任何帮助,将不胜感激!

谢谢 :)

typ*_*eof 196

您可以使用对象来保存结果:

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]);
Run Code Online (Sandbox Code Playgroud)

所以,现在你的计数对象可以告诉你特定数字的计数:

console.log(counts[5]); // logs '3'
Run Code Online (Sandbox Code Playgroud)

如果您想获得一组成员,只需使用这些keys()功能即可

keys(counts); // returns ["5", "2", "9", "4"]
Run Code Online (Sandbox Code Playgroud)

  • 同样,我也喜欢`counts [num] =(counts [num] || 0)+ 1`.这样你只需要在那一行写两次`count [num]`而不是三次. (15认同)
  • 需要指出的是,只有IE9 +,FF4 +,SF5 +,CH6 +支持`Object.keys()`函数,但Opera不支持它.我认为这里最大的显示器是**IE9 +**. (3认同)
  • 这是一个很好的答案。这很容易抽象为一个函数,该函数接受一个数组并返回一个“计数”对象。 (2认同)

Šim*_*das 87

干得好:

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];
}
Run Code Online (Sandbox Code Playgroud)

现场演示: http ://jsfiddle.net/simevidas/bnACW/

注意

这会改变原始输入数组的顺序 Array.sort

  • 有排序数组的副作用(副作用是坏的),排序是'O(N log(N))`和优雅的收益是不值得的 (18认同)

ada*_*mse 81

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}
Run Code Online (Sandbox Code Playgroud)

  • `acc [curr]?acc [curr] ++:acc [curr] = 1;` (32认同)
  • 谢谢,非常好的解决方案;) ...并获取“键”和“值”数组:`constkeys = Object.keys(a);`constvalues=Object.values(a);` (2认同)

rad*_*and 70

如果使用下划线或lodash,这是最简单的事情:

_.countBy(array);
Run Code Online (Sandbox Code Playgroud)

这样:

_.countBy([5, 5, 5, 2, 2, 2, 2, 2, 9, 4])
=> Object {2: 5, 4: 1, 5: 3, 9: 1}
Run Code Online (Sandbox Code Playgroud)

正如其他人所指出的那样,您可以在结果上执行_.keys()_.values()函数,以分别获得唯一的数字及其出现次数.但根据我的经验,原始对象更容易处理.

  • `,_.identity`是可选的.你可以写`_.countBy(array)` (8认同)

mu *_*ort 53

不要使用两个数组作为结果,使用一个对象:

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]];
}
Run Code Online (Sandbox Code Playgroud)

然后result会看起来像:

{
    2: 5,
    4: 1,
    5: 3,
    9: 1
}
Run Code Online (Sandbox Code Playgroud)


Emi*_*ary 37

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]
));
Run Code Online (Sandbox Code Playgroud)
aCount.get(5)  // 3
aCount.get(2)  // 5
aCount.get(9)  // 1
aCount.get(4)  // 1
Run Code Online (Sandbox Code Playgroud)

此示例将输入数组传递给Set构造函数,从而创建唯一值的集合.该价差语法则扩展了这些值到一个新的数组,所以我们可以调用map并转化为一个二维数组这[value, count]对-即结构如下:

Array [
   [5, 3],
   [2, 5],
   [9, 1],
   [4, 1]
]
Run Code Online (Sandbox Code Playgroud)

然后将新数组传递给Map构造函数,从而生成一个可迭代对象:

Map {
    5 => 3,
    2 => 5,
    9 => 1,
    4 => 1
}
Run Code Online (Sandbox Code Playgroud)

关于Map对象的好处是它保留了数据类型 - 也就是说aCount.get(5)将返回3aCount.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));
Run Code Online (Sandbox Code Playgroud)

  • 这可能使用了不错的新数据结构,但在O(*n²*)中具有运行时,而这里有许多简单的算法可以在O(* n *)中解决它。 (6认同)

小智 34

我认为这是如何计算数组中具有相同值的事件的最简单方法.

var a = [true, false, false, false];
a.filter(function(value){
    return value === false;
}).length
Run Code Online (Sandbox Code Playgroud)

  • 或者使用新的js语法的`a.filter(value =>!value).length` (8认同)
  • 没有回答问题。 (3认同)

Vla*_*den 27

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))
Run Code Online (Sandbox Code Playgroud)

  • 有人愿意解释这个(prev [curr] = ++ prev [curr] || 1,prev)? (3认同)
  • [逗号运算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator#Processing_and_then_returning)"评估每个操作数(从左到右)并返回值最后一个操作数",所以这会增加prev [curr]的值(或将其初始化为1),然后返回prev. (3认同)

rja*_*lfa 20

如果您喜欢单个衬垫.

arr.reduce(function(countMap, word) {countMap[word] = ++countMap[word] || 1;return countMap}, {});

编辑(2015年12月6日):由内而外的解释.countMap是一个映射单词及其频率的映射,我们可以看到匿名函数.reduce的作用是将带有参数的函数应用为所有数组元素和countMap作为最后一个函数调用的返回值传递.最后一个参数({})是第一个函数调用的countMap的默认值.

  • 一个单个衬垫,仅删除通常跟随在“;”、“{”和“}”之后的换行符。... 好的。我认为根据单行的定义,我们可以将康威的生命游戏写为“单行”。 (2认同)

Ngu*_*ong 16

2021 年的版本

更优雅的方法是Logical nullish assignment (x ??= y) 结合Array#reduce()O(n) 时间复杂度使用。

其主要思想仍然使用Array#reduce()与输出聚集为object以获得最高的性能(时间和空间复杂度)中的条款searchingconstruct bunches of intermediate arrays像其他的答案。

const arr = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];
const result = arr.reduce((acc, curr) => {
  acc[curr] ??= {[curr]: 0};
  acc[curr][curr]++;
  
  return acc;
}, {});

console.log(Object.values(result));
Run Code Online (Sandbox Code Playgroud)

清理和重构代码

使用逗号运算符 (,)语法。

The comma operator (,)计算其每个操作数(从左到右)并返回最后一个操作数的值

const arr = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];
const result = arr.reduce((acc, curr) => (acc[curr] = (acc[curr] || 0) + 1, acc), {});
console.log(result);
Run Code Online (Sandbox Code Playgroud)

输出

{
  "2": 5,
  "4": 1,
  "5": 3,
  "9": 1
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常好的答案。您可以使用以下命令使其更加简洁: `const result = arr.reduce((acc, curr) =&gt; (acc[curr] = -~(acc[curr]), acc), {});` 请参阅 https:// /stackoverflow.com/a/47546846/1659476 进行解释。 (3认同)

Wil*_*ung 15

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 }
Run Code Online (Sandbox Code Playgroud)

Map而不是plain Object帮助我们区分不同类型的元素,或者所有计数都基于字符串


Pen*_*Liu 11

reduce使用and tilde( ~) 运算符的较短版本。

const data = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];

function freq(nums) {
  return nums.reduce((acc, curr) => {
    acc[curr] = -~acc[curr];
    return acc;
  }, {});
}

console.log(freq(data));
Run Code Online (Sandbox Code Playgroud)


小智 8

如果您使用下划线,您可以使用功能路线

a = ['foo', 'foo', 'bar'];

var results = _.reduce(a,function(counts,key){ counts[key]++; return counts },
                  _.object( _.map( _.uniq(a), function(key) { return [key, 0] })))
Run Code Online (Sandbox Code Playgroud)

所以你的第一个阵列是

_.keys(results)
Run Code Online (Sandbox Code Playgroud)

第二个数组是

_.values(results)
Run Code Online (Sandbox Code Playgroud)

如果它们可用,大部分将默认为本机javascript函数

演示:http://jsfiddle.net/dAaUU/


cor*_*ina 8

一线ES6解决方案。使用对象作为地图的答案如此之多,但我看不到有人使用实际的地图

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

使用map.keys()获得独特的元素

使用map.values()来获取事件

使用map.entries()以获得对[元件,频率]

我参加聚会有点晚了,但我希望至少有人会有所帮助。

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


ESL*_*ESL 6

基于答案@adamse@pmandell(我给予好评),在ES6你能做到这一点的一条线:

  • 2017编辑:我||用来减少代码大小并使其更具可读性.

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},{})

));
Run Code Online (Sandbox Code Playgroud)


它可以用来计算字符数:

var s="ABRACADABRA";
alert(JSON.stringify(

s.split('').reduce((a, c)=>{a[c]++?0:a[c]=1;return a},{})

));
Run Code Online (Sandbox Code Playgroud)


Sar*_*dor 6

使用时间复杂度为O(n)的映射的解决方案。

var arr = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];

const countOccurrences = (arr) => {
    const map = {};
    for ( var i = 0; i < arr.length; i++ ) {
        map[arr[i]] = ~~map[arr[i]] + 1;
    }
    return map;
}
Run Code Online (Sandbox Code Playgroud)

演示: http: //jsfiddle.net/simvidas/bnACW/


Koo*_*Inc 5

您可以扩展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
Run Code Online (Sandbox Code Playgroud)

或者你可以利用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;
  }
Run Code Online (Sandbox Code Playgroud)


ElD*_*239 5

这里只是一些轻松易用的东西......

function count(a,i){
 var result = 0;
 for(var o in a)
  if(a[o] == i)
   result++;
 return result;
}
Run Code Online (Sandbox Code Playgroud)

编辑:因为你想要所有的出现......

function count(a){
 var result = {};
 for(var i in a){
  if(result[a[i]] == undefined) result[a[i]] = 0;
  result[a[i]]++;
 }
 return result;
}
Run Code Online (Sandbox Code Playgroud)


SoE*_*zPz 5

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};
Run Code Online (Sandbox Code Playgroud)

如果你仍然想要两个数组,那么你可以使用这样的答案......

var uniqueNums = Object.keys(answer);
// uniqueNums => ["2", "4", "5", "9"];

var countOfNums = Object.keys(answer).map(key => answer[key]);
// countOfNums => [5, 1, 3, 1];
Run Code Online (Sandbox Code Playgroud)

或者,如果您希望 uniqueNums 是数字

var uniqueNums = Object.keys(answer).map(key => +key);
// uniqueNums => [2, 4, 5, 9];
Run Code Online (Sandbox Code Playgroud)


Gar*_*ner 5

So here's how I'd do it with some of the newest javascript features:

First, reduce the array to a Map of the counts:

let countMap = array.reduce(
  (map, value) => {map.set(value, (map.get(value) || 0) + 1); return map}, 
  new Map()
)
Run Code Online (Sandbox Code Playgroud)

通过使用Map,起始数组可以包含任何类型的对象,并且计数将是正确的。如果不使用Map,则某些类型的对象会给您带来奇怪的计数。有关差异的更多信息,请参阅Map文档

如果您所有的值都是符号,数字或字符串,则也可以使用一个对象来完成此操作:

let countObject = array.reduce(
  (map, value) => { map[value] = (map[value] || 0) + 1; return map },
  {}
)
Run Code Online (Sandbox Code Playgroud)

或者使用解构和对象散布语法以某种功能性稍稍稍稍变而无变化:

let countObject = array.reduce(
  (value, {[value]: count = 0, ...rest}) => ({ [value]: count + 1, ...rest }),
  {}
)
Run Code Online (Sandbox Code Playgroud)

此时,您可以将Mapor对象用于计数(与对象不同,地图可以直接迭代),或将其转换为两个数组。

对于Map

countMap.forEach((count, value) => console.log(`value: ${value}, count: ${count}`)

let values = countMap.keys()
let counts = countMap.values()
Run Code Online (Sandbox Code Playgroud)

或针对对象:

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)
Run Code Online (Sandbox Code Playgroud)