我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道2
数组中有多少s。
在没有循环的情况下,在 JavaScript 中最优雅的方法是什么for
?
我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道2
数组中有多少s。
在没有循环的情况下,在 JavaScript 中最优雅的方法是什么for
?
[这个答案有点过时:阅读编辑]
向你的朋友问好:map
and filter
and reduce
and forEach
and every
etc.
(我只是偶尔在 javascript 中编写 for 循环,因为缺少块级作用域,所以如果你需要捕获或克隆你的迭代索引或值,无论如何你必须使用一个函数作为循环体。For-loops通常更有效,但有时你需要一个闭包。)
最易读的方式:
[....].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年编辑:
oop,这个答案比正确答案更受欢迎。实际上,只需使用已接受的答案即可。虽然这个答案可能很可爱,但 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;
}
}
});
您可以定义一个.countStrictEq(...)
使用===
平等概念的版本。平等的概念可能对你正在做的事情很重要!(例如[1,10,3,'10'].count(10)==2
,因为在 javascript 中像 '4'==4 这样的数字......因此调用它.countEq
或.countNonstrict
强调它使用==
运算符。)
警告:在原型上定义一个通用名称应该小心。如果你控制你的代码很好,但如果每个人都想声明自己的
[].count
函数就不好了,尤其是当他们的行为不同时。您可能会问自己“但.count(query)
听起来确实非常完美和规范”……但考虑一下,也许您可以做类似[].count(x=> someExpr of x)
. 在这种情况下,您可以定义类似countIn(query, container)
(undermyModuleName.countIn
)、或某物或[].myModuleName_count()
.
还可以考虑使用您自己的多集数据结构(例如,python 的 ' collections.Counter
')以避免首先进行计数。这适用于表单的完全匹配[].filter(x=> x==???).length
(最坏的情况O(N)到O(1)),修改后将加速表单的查询[].filter(filterFunction).length
(大约是#total/#duplicates的因子)。
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 的一次性单行代码),现在您可以更简洁地将其编写为[...].filter(x => x==2).length
. 如果您关心性能,请注意,虽然这与 for 循环(O(N) 时间)的性能渐近相同,但它可能需要 O(N) 额外内存(而不是 O(1) 内存),因为它几乎当然生成一个中间数组,然后计算该中间数组的元素。
现代 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 中的以下匿名箭头函数(lambda 函数):
(x) => {
const k = 2
return k * x
}
对于单个输入,可以简化为这种简洁的形式:
x => 2 * x
其中return
的暗示。
始终使用三重等号:===
用于 JS 中的比较,但检查可空性时除外:if (something == null) {}
因为您包括检查undefined
是否只使用双等号,如本例。
很简单的:
var count = 0;
for(var i = 0; i < array.length; ++i){
if(array[i] == 2)
count++;
}
2017: 如果有人仍然对这个问题感兴趣,我的解决方案如下:
const arrayToCount = [1, 2, 3, 5, 2, 8, 9, 2];
const result = arrayToCount.filter(i => i === 2).length;
console.log('number of the found elements: ' + result);
如果您使用 lodash 或下划线,_.countBy方法将提供一个由数组中的每个值键控的聚合总数对象。如果您只需要计算一个值,您可以将其转换为单行:
_.countBy(['foo', 'foo', 'bar'])['foo']; // 2
这也适用于数字数组。您的示例的单行是:
_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3