对数组中的项目进行分组和计数的简洁方法

Posted

技术标签:

【中文标题】对数组中的项目进行分组和计数的简洁方法【英文标题】:Concise way to Group by and Count item in array 【发布时间】:2021-06-27 08:04:50 【问题描述】:

我有一个这样的数组:

const arr = [ name: 'Server 1', country: 'DE', name: 'Server 2', country: 'PL', 
              name: 'Server 3', country: 'US', name: 'Server 4', country: 'DE', 
              name: 'Server 5', country: 'US'];

我想要的是group and count 得到如下输出:

 [
  
    "country": "DE",
    "count": 2
  ,
  
    "country": "PL",
    "count": 1
  ,
  
    "country": "US",
    "count": 2
  
]

目前,我正在使用lodash,但我认为有更好的方法(例如,使用_groupBy 或类似的东西) 来解决它,对吧?

我的代码在这里:

const arr = [ name: 'Server 1', country: 'DE', name: 'Server 2', country: 'PL', name: 'Server 3', country: 'US', name: 'Server 4', country: 'DE', name: 'Server 5', country: 'US'];

const objectGroupby = _.countBy(arr, 'country');
const result = Object.entries(objectGroupby).map(([key, value]) => (country: key, count: value));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

如您所见,_.countBy(arr, 'country') 只是返回一个对象而不是数组。


  "DE": 2,
  "PL": 1,
  "US": 2

那我得用Object.entries()&map来解决。

【问题讨论】:

【参考方案1】:

在性能方面,我认为我可以使用简单的for loops as for loops are faster than .map(), .reduce() 等编写性能更高的代码。

遍历原数组一次,可以做到以下几点,

const arr = [ name: 'Server 1', country: 'DE', name: 'Server 2', country: 'PL', name: 'Server 3', country: 'US', name: 'Server 4', country: 'DE', name: 'Server 5', country: 'US'];

let mapObj = ;
let res = [];
let resIndex = 0;

for(let i = 0; i < arr.length; i++) 
  if(mapObj[arr[i].country] >= 0) 
    res[mapObj[arr[i].country]].count++;
   else 
    res.push(country: arr[i].country, count: 1);
    mapObj[arr[i].country] = resIndex;
    resIndex++;
  


console.log(res);

就优雅或更易读的代码而言,我认为使用reduce 更易读。但可读性是主观的,因人而异。对我来说,reduce 会更具可读性。

const arr = [ name: 'Server 1', country: 'DE', name: 'Server 2', country: 'PL', name: 'Server 3', country: 'US', name: 'Server 4', country: 'DE', name: 'Server 5', country: 'US'];

res = arr.reduce((prev, curr) => 
  const index = prev.findIndex(item => item.country === curr.country);
  if(index > -1) 
    prev[index].count++;
   else 
    prev.push( country: curr.country, count: 1);
  
  return prev;
, []);

console.log(res);

更新: 使用 Lodash,

const arr = [ name: 'Server 1', country: 'DE', name: 'Server 2', country: 'PL', name: 'Server 3', country: 'US', name: 'Server 4', country: 'DE', name: 'Server 5', country: 'US'];

result = _.reduce(_.countBy(arr, 'country'), (result, value, key) => 
  result.push( country: key, count: value);
  return result;
, []);

console.log(result);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"&gt;&lt;/script&gt;

【讨论】:

感谢您的回答。但我想到了除_.countBy 之外的另一个lodash's method。无论如何 +1 尊重。 嗨,我已经更新了我的答案。我认为 'lodash' 中的 _.reduce() 可以用来编写更紧凑和可读的代码。请看一看。 是的,看起来不错。就个人而言,在这种情况下使用Array#reduce 是最简洁的。谢谢。 在性能方面,你觉得Object.entries() + map_.countBy(arr, 'country')之间的_.reduce之间怎么样?我的意思是 Object.entries 实际上是做什么的? 根据我的观察,Object.entries() 需要一次迭代,然后您使用 map 进行另一次迭代。但是对于`_.reduce()`,您只使用了一次迭代。这取决于 JS 引擎的实现。但大致它会遍历你的对象并返回一个数组。你可以在这里看到developer.mozilla.org/en-US/docs/Web/javascript/Reference/…。【参考方案2】:

万岁!!!

经过最近几天的研究,我终于想出了像这样使用groupBy的解决方案。

const arr = [ name: 'Server 1', country: 'DE', name: 'Server 2', country: 'PL', name: 'Server 3', country: 'US', name: 'Server 4', country: 'DE', name: 'Server 5', country: 'US'];
const result = _(arr).groupBy(x => x.country)
                     .map((value, key) => (country: key, count: value.length)); 
console.log(result);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"&gt;&lt;/script&gt;

说明:

    第一步:根据country属性对数组元素进行分组 第 2 步:使用具有 2 个参数值的 .map:key 是组的名称(国家/地区),value 是对象数组。

参考资源:

using lodash .groupBy. how to add your own keys for grouped output?

【讨论】:

以上是关于对数组中的项目进行分组和计数的简洁方法的主要内容,如果未能解决你的问题,请参考以下文章

Mongodb计数数组中的项目组合

SQL - 如何在一年中的每个日期按年龄和状态对项目进行分组/计数 - 第 2 部分

对数组进行分组和计数

Linq 按连接表中的项目计数分组

Eclipse中,对工作空间中的项目进行分组

在 Pandas 中分组和计数