如何计算数组中的某些元素?
我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道数组中有多少个s。2
在JavaScript中不循环使用循环的最优雅方法是什么?for
我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道数组中有多少个s。2
在JavaScript中不循环使用循环的最优雅方法是什么?for
[这个答案有点过时了:阅读编辑]
向你的朋友问好:和和等等。map
filter
reduce
forEach
every
(我只是偶尔在javascript中编写for-loops,因为缺少块级范围,所以如果你需要捕获或克隆你的迭代索引或值,你必须使用一个函数作为循环的主体。For 循环通常效率更高,但有时您需要一个闭包。
最易读的方式:
[....].filter(x => x==2).length
(我们本来可以写).filter(function(x){return x==2}).length
以下内容更节省空间(O(1)而不是O(N)),但我不确定您可能会在时间方面支付多少好处/损失(不超过一个常数因子,因为您访问每个元素一次):
[....].reduce((total,x) => (x==2 ? total+1 : total), 0)
(如果您需要优化这段特定的代码,for循环在某些浏览器上可能会更快...你可以在 jsperf.com 上测试一些东西。
然后,您可以变得优雅并将其转换为原型函数:
[1, 2, 3, 5, 2, 8, 9, 2].count(2)
喜欢这个:
Object.defineProperties(Array.prototype, {
count: {
value: function(value) {
return this.filter(x => x==value).length;
}
}
});
您还可以将常规的旧for循环技术(请参阅其他答案)粘贴到上述属性定义中(同样,这可能会快得多)。
2017年编辑:
哎呀,这个答案比正确答案更受欢迎。实际上,只需使用可接受的答案即可。虽然这个答案可能很可爱,但js编译器可能不会(或由于规范而无法)优化这种情况。所以你真的应该写一个简单的for循环:
Object.defineProperties(Array.prototype, {
count: {
value: function(query) {
/*
Counts number of occurrences of query in array, an integer >= 0
Uses the javascript == notion of equality.
*/
var count = 0;
for(let i=0; i<this.length; i++)
if (this[i]==query)
count++;
return count;
}
}
});
你可以定义一个使用平等概念的版本。平等的概念可能对你正在做的事情很重要!(例如,因为像'4'==4这样的数字在javascript中...因此调用它或强调它使用运算符。.countStrictEq(...)
===
[1,10,3,'10'].count(10)==2
.countEq
.countNonstrict
==
警告:在原型上定义通用名称应小心完成。如果您控制代码,这很好,但如果每个人都想声明自己的函数,特别是如果它们的行为不同,那就不好了。你可能会问自己“但听起来肯定很完美和规范”......但考虑也许你可以做这样的事情。在这种情况下,您可以定义诸如(under)之类的函数,或者其他功能,或者.
[].count
.count(query)
[].count(x=> someExpr of x)
countIn(query, container)
myModuleName.countIn
[].myModuleName_count()
还要考虑使用自己的多集数据结构(例如python的''),以避免首先进行计数。这适用于表单的完全匹配(最坏情况 O(N) 到 O(1)),修改后将加快表单的查询速度(大约是 #total/#duplicates)。collections.Counter
[].filter(x=> x==???).length
[].filter(filterFunction).length
class Multiset extends Map {
constructor(...args) {
super(...args);
}
add(elem) {
if (!this.has(elem))
this.set(elem, 1);
else
this.set(elem, this.get(elem)+1);
}
remove(elem) {
var count = this.has(elem) ? this.get(elem) : 0;
if (count>1) {
this.set(elem, count-1);
} else if (count==1) {
this.delete(elem);
} else if (count==0)
throw `tried to remove element ${elem} of type ${typeof elem} from Multiset, but does not exist in Multiset (count is 0 and cannot go negative)`;
// alternatively do nothing {}
}
}
演示:
> counts = new Multiset([['a',1],['b',3]])
Map(2) {"a" => 1, "b" => 3}
> counts.add('c')
> counts
Map(3) {"a" => 1, "b" => 3, "c" => 1}
> counts.remove('a')
> counts
Map(2) {"b" => 3, "c" => 1}
> counts.remove('a')
Uncaught tried to remove element a of type string from Multiset, but does not exist in Multiset (count is 0 and cannot go negative)
旁注:不过,如果你仍然想要函数式编程方式(或者一个不覆盖 Array.prototype 的一次性单行代码),你现在可以更简洁地把它写成 .如果您关心性能,请注意,虽然这与 for 循环(O(N) 时间)渐近时具有相同的性能,但它可能需要 O(N) 额外内存(而不是 O(1) 内存),因为它几乎肯定会生成一个中间数组,然后计算该中间数组的元素。[...].filter(x => x==2).length
现代 JavaScript:
请注意,在 JavaScript (JS) 中进行比较时,应始终使用三重相等。三重等于确保JS比较的行为类似于其他语言中的双相等(有一个例外,见下文)。以下解决方案显示了如何以功能方式解决此问题,这将确保您永远不会拥有:===
==
out of bounds error
// Let has local scope
let array = [1, 2, 3, 5, 2, 8, 9, 2]
// Functional filter with an Arrow function
array.filter(x => x === 2).length // -> 3
JavaScript 中的以下匿名 Arrow 函数(lambda 函数):
(x) => {
const k = 2
return k * x
}
对于单个输入,可以简化为这种简洁的形式:
x => 2 * x
其中隐含。return
始终使用三重相等:用于JS中的比较,除了检查可空性时:因为如果您只使用双精度相等,则包括检查,如本例所示。===
if (something == null) {}
undefined