统计 JavaScript 数组元素的出现次数并放入一个新的二维数组中。

3

你好,我有一个像这样的数组

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

我的目标是计算唯一值并生成有关其中任何项的报告,因此结果将如下:

Array [
   [5, 3],
   [2, 5],
   [9, 1],
   [4, 1]
]

我找到了一个解决方案,可以在这篇文章中找到[计算JavaScript数组元素出现次数

对我来说,@Emissary的解决方案是最好的,问题是这个解决方案添加了一些我不需要的新功能,而且我无法直接回复该帖子询问如何只获取我需要的数组:D
@Emissary 添加了

console.log(key + ': ' + val)

我的第一个想法是,不使用console.log,我可以将每个值推入2d数组,但我认为这不是一个好主意,因为如果我理解正确,@Emissary的解决方案的第一部分正好创建了我需要的数组。
有什么办法可以“隔离”这个特定的数组吗?
7个回答

5

这是使用Map的理想情况,因为它可以通过键来识别,但在转换为该类型的数组时也很方便:

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

var result = [...a.reduce( (m, v) => m.set(v, (m.get(v) || 0) + 1), new Map() )];

console.log(result);

请注意,每次迭代执行a.filter的解决方案具有O(n²)时间复杂度,而这是O(n)。您可以使用Array.from(a.reduce(....))而不是扩展运算符。
对于旧版浏览器,您可以使用以下变体:

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

var obj = a.reduce( function (m, v) { return m[v] = (m[v] || 0) + 1, m }, {} ),
    result = [];
for (var key in obj) {
    result.push([+key, obj[key]]);
}
console.log(result);


1
使用reduce是在性能方面最佳的解决方案。 - loretoparisi
谢谢,看起来很快。我只注意到它使用了扩展语法,而我使用的是旧版浏览器Iceweasel,不知道是否可行。目前仅在Windows上测试过node,扩展语法无法使用。 - Jorman Franzini
你需要哪种支持?ES3,ES5?你是否支持Map - trincot
@Redu,你为什么说是 O(n²)?它不是。O(n) + O(n) 仍然是 *O(n)*。你似乎在某个地方进行了乘法,但实际上并没有。首先是 reduce,然后是 spread。这是 *O(n)*。 - trincot
谢谢,我不知道这个运行在哪个浏览器上,因为它是一个多平台实现,所以我必须考虑最新和最旧的电脑 :D 我只认为在这一点上性能是关键,我必须检查哪一个是最快的。谢谢 - Jorman Franzini
显示剩余7条评论

3
你可以使用 forEach() 循环来完成这个操作。

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

var result = []
a.forEach(function(e) {
  if (!this[e]) {
    this[e] = [e, 0];
    result.push(this[e])
  }
  this[e][1] ++
}, {})

console.log(result)


2
您可以使用ES6的Mapmap来完成它,然后使用Array.from array.from将其转换回数组。
Map类似于哈希,它维护键值对。不需要遍历每个元素并维护计数器,只需创建一个地图并设置每个出现次数的计数,然后使用es6将其转换为数组即可。
请查看此片段。

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var myMap2 = new Map();
a.forEach(function(num) {
  if (myMap2.has(num)) {
    var count = myMap2.get(num);
    myMap2.set(num, count + 1);
  } else
    myMap2.set(num, 1);
});

var arr = Array.from(myMap2);
console.log(arr);

希望有所帮助。

谢谢,这个解决方案没有使用扩展语法,在我的情况下更好,因为我需要确保最佳兼容性。 - Jorman Franzini

1
只需将其从 Map 构造函数中提取出来。

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var aCount = [...new Set(a)].map(
  x => [x, a.filter(y => y === x).length]
);
console.log(aCount);
   


0
你可以使用扩展语法Emissary的解决方案来获得所需结果的数组。

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

var aCount = [... new Map([... new Set(a)].map(
    x => [x, a.filter(y => y === x).length]
))];
              
console.log(aCount);


0

这里有很好的解决方案,但为了多样性,您也可以按照以下步骤进行操作,以便对结果进行排序

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
thing = a.reduce((p,c) => (p[c] ? p[c]++ : p[c] = 1,p),[])
         .reduce((p,c,i) => c ? (p.push([i,c]),p) : p ,[]);
console.log(thing);

好的...有人可能会抱怨根据原始数字生成一个巨大的稀疏数组的可能性,但是别担心...for in循环会处理这个问题。所以让我们再做一遍...

var  a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
sparse = a.reduce((p,c) => (p[c] ? p[c]++ : p[c] = 1,p),[]),
 dense = [];
for (var key in sparse) dense.push([+key,sparse[key]]);
console.log(dense);


0
有趣的是,当结合 map 转换输出列时,reducespread syntax 解决方案在评估文本中单词出现次数/频率的数组(通常是词袋模型)时也非常高效。

var r,words="We don't talk anymore\nWe don't talk anymore\nWe don't talk anymore\nLike we used to do\nWe don't love anymore\nWhat was all of it for?\nOh, we don't talk anymore\nLike we used to do\n\nI just heard you found the one you've been looking\nYou've been looking for"
words=words.split(/['\s]+/g);
r=[...words.reduce( (m, v) => m.set(v, ((m.get(v) || 0) + 1)), new Map() )].map(e=>[e[0],e[1]/words.length])
console.log(r)


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