统计数组元素出现次数/频率

352

在 Javascript 中,我试图获取一个数字值的初始数组并计算其中的元素数量。理想情况下,结果会是两个新数组,第一个指定每个唯一元素,第二个包含每个元素出现的次数。但是,对于输出格式,我也很乐意听取建议。

例如,如果初始数组如下:

5, 5, 5, 2, 2, 2, 2, 2, 9, 4

然后将创建两个新数组。第一个数组将包含每个唯一元素的名称:

5, 2, 9, 4
第二个数组将包含该元素在初始数组中出现的次数:
3, 5, 1, 1
因为数字5出现了三次,数字2出现了五次,数字9和4各出现了一次。
我已经搜索过很多解决方案,但似乎没有一个可行的,并且我自己尝试的所有方法都变得非常复杂。任何帮助将不胜感激!
谢谢 :)

16
如果你只需要判断一个值是否仅出现一次(而不是两次或更多),你可以使用 if (arr.indexOf(value) == arr.lastIndexOf(value)) - Rodrigo
2
我们可以使用 ramda.js 来轻松实现这一点。R.countBy(r=> r)(ary)``` - Eshwar Prasad Yaddanapudi
arr.filter(x => x===5).length 会返回 3,表示数组中有 '3' 个数字 '5'。 - noobninja
假设我的响应是对象数组。 - Ajay
42个回答

3

使用 ramda.js,我们可以更好、更简单地实现这一点。

const ary = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]; R.countBy(r=> r)(ary) 可在此处查看代码示例:链接

countBy 的文档在此处:链接


3

我知道这个问题很老,但我意识到很少有解决方案可以像要求的那样使用最少的代码来获取计数数组,所以这是我的代码:

// The initial array we want to count occurences
var initial = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];  

// The count array asked for
var count = Array.from(new Set(initial)).map(val => initial.filter(v => v === val).length);  

// Outputs [ 3, 5, 1, 1 ]

此外,您可以使用以下方法从原始数组中获取集合:

var set = Array.from(new Set(initial));  

//set = [5, 2, 9, 4]  

这段代码非常低效,因为它需要对初始数组进行长度的平方次迭代。 - Domino
是的,这段代码的时间复杂度是length²,这就是为什么我坚持它的目的是提供一个解决问题的最小化代码 - al kaj

3

返回一个可排序的数组:

let array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
let reducedArray = array.reduce( (acc, curr, _, arr) => {
    if (acc.length == 0) acc.push({item: curr, count: 1})
    else if (acc.findIndex(f => f.item === curr ) === -1) acc.push({item: curr, count: 1})
    else ++acc[acc.findIndex(f => f.item === curr)].count
    return acc
}, []);

console.log(reducedArray.sort((a,b) => b.count - a.count ))

/*
  Output:
  [
    {
      "item": 2,
      "count": 5
    },
    {
      "item": 5,
      "count": 3
    },
    {
      "item": 9,
      "count": 1
    },
    {
      "item": 4,
      "count": 1
    }
  ]

*/


2
给定下面提供的数组:
const array = [ 'a', 'b', 'b', 'c', 'c', 'c' ];

你可以使用这个简单的一行代码来生成一个哈希映射,将一个键链接到它在数组中出现的次数:
const hash = Object.fromEntries([ ...array.reduce((map, key) => map.set(key, (map.get(key) || 0) + 1), new Map()) ]);
// { a: 1, b: 2, c: 3 }

扩展和解释:

// first, we use reduce to generate a map with values and the amount of times they appear
const map = array.reduce((map, key) => map.set(key, (map.get(key) || 0) + 1), new Map())

// next, we spread this map into an array
const table = [ ...map ];

// finally, we use Object.fromEntries to generate an object based on this entry table
const result = Object.fromEntries(table);

感谢@corashina提供的array.reduce代码


2

使用 Lodash

const values = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
const frequency = _.map(_.groupBy(values), val => ({ value: val[0], frequency: val.length }));
console.log(frequency);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


2
使用MAP,您可以在输出中有两个数组:一个包含出现次数,另一个包含出现次数的数量。

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)


2
这个问题已经超过8年了,很多回答并没有考虑ES6及其众多优势。当我们创建额外的数组、复制数组两三次甚至将数组转换为对象时,更重要的是考虑代码对垃圾收集/内存管理的影响。这对于小型应用程序来说可能是微不足道的观察,但如果规模是长期目标,那么请仔细考虑它们。如果您只需要特定数据类型的"计数器"(有序列表),并且起点是一个数组,则可以简单地迭代数组1并使用其中的值和出现次数填充数组2。就像那样简单。这里有一个简单类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;

2
将一个函数无缘无故地放入一个类中并不能使其面向对象化,finalList 没有理由成为一个数组,这样做与正确的方法相比没有任何优势。 - Ry-

2

我使用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)

Link to REPL.


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

每次复制对象都非常浪费资源,这会导致二次最坏情况的出现,而本可以是线性的。 - Ry-

1
请查看下面的代码。
<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>

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