我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道数组中有多少个2
。
在JavaScript中,如何不使用for
循环以最简洁的方式实现?
我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道数组中有多少个2
。
在JavaScript中,如何不使用for
循环以最简洁的方式实现?
[这个答案有点过时:请阅读编辑,因为在JavaScript中“相等”的概念是模棱两可的]
向你的朋友们打个招呼: map
、filter
、reduce
、forEach
和every
等等。
(我只偶尔在JavaScript中编写for循环,因为缺少块级作用域,所以如果需要捕获或克隆迭代索引或值,则必须将函数用作循环的主体。通常情况下,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)
或者像一位评论者友好地指出的那样:
[....].reduce((total,x) => total+(x==2), 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;
}
}
});
你可以定义一个名为.countStrictEq(...)
的版本,它使用===
相等性的概念。相等性的概念可能对你正在做的事情很重要!(例如[1,10,3,'10'].count(10)==2
,因为在Javascript中像'4'==4...因此称其为.countEq
或.countNonstrict
会强调它使用==
运算符)。[].count
函数,特别是如果它们的行为不同,那么就会有问题。你可能会问自己“但是.count(query)
听起来很完美和经典”...但是考虑一下你是否可以做类似于[].count(x=> someExpr of x)
的事情。在这种情况下,你可以定义像countIn(query, container)
这样的函数(在myModuleName.countIn
下),或者像[].myModuleName_count()
这样的东西。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比较的行为类似于其他语言中的双等号 ==
(有一个例外,请参见下文)。以下解决方案展示了如何以函数式的方式解决这个问题,这将确保您永远不会出现越界错误
:
// Let has local scope
let array = [1, 2, 3, 5, 2, 8, 9, 2]
// Functional filter with an Arrow function
// Filter all elements equal to 2 and return the length (count)
array.filter(x => x === 2).length // -> 3
下面是JavaScript中的匿名箭头函数(lambda函数):
(x) => {
const k = 2
return k * x
}
可以将其简化为以下针对单个输入的简洁形式:
x => 2 * x
在JS中,如果要进行比较,请始终使用三个等号:===
,但当检查是否为空时除外:if (something == null) {}
,因为在这种情况下仅使用双等号包括对undefined
的检查。
其中return
是隐含的。
reduce
解决,下面的答案之一就是这样做的。但当我回答这个问题时,我考虑到了正在学习 JavaScript 的人,并因此选择了 filter
函数。 - Sverrisson非常简单:
var count = 0;
for(var i = 0; i < array.length; ++i){
if(array[i] == 2)
count++;
}
const count = countItems(array, 2);
,实现细节可以在内部进行讨论。 - joeytwiddle2017年:如果有人仍然对这个问题感兴趣,我的解决方案如下:
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);
这里是一种 ES2017+ 的方法,以 O(N) 的时间复杂度获取所有数组项的计数:
const arr = [1, 2, 3, 5, 2, 8, 9, 2];
const counts = {};
arr.forEach((el) => {
counts[el] = counts[el] ? (counts[el] + 1) : 1;
});
您还可以选择对输出进行排序:
const countsSorted = Object.entries(counts).sort(([_, a], [__, b]) => a - b);
console.log(countsSorted)
对于你的示例数组:
[
[ '2', 3 ],
[ '1', 1 ],
[ '3', 1 ],
[ '5', 1 ],
[ '8', 1 ],
[ '9', 1 ]
]
counts.hasOwnProperty(el)
is safer than counts[el]
because, while not applicable here, zero is treated as false, so if counts[el] = 0
, then counts[el] == false
- Edwarric如果您正在使用lodash或underscore,_.countBy方法将提供一个对象,其中包含按数组中每个值为键的聚合总数。如果您只需要计算一个值,可以将其转换为一行代码:
_.countBy(['foo', 'foo', 'bar'])['foo']; // 2
这个方法同样适用于数字数组。你的示例可以采用以下一行代码:
_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3
(a.length-(' '+a.join(' ')+' ').split(' '+n+' ').join(' ').match(/ /g).length)+1
在这里:
我的建议是使用 while 或 for 循环 ;-)
不使用循环通常意味着将过程交给某个使用循环的方法。
这里有一种方式,我们讨厌循环的程序员可以满足他的厌恶,但需要付出代价:
var a=[1, 2, 3, 5, 2, 8, 9, 2];
alert(String(a).replace(/[^2]+/g,'').length);
/* returned value: (Number)
3
*/
function countItems(arr, what){
var count= 0, i;
while((i= arr.indexOf(what, i))!= -1){
++count;
++i;
}
return count
}
countItems(a,2)
/* returned value: (Number)
3
*/
String(a).match(/2/g).length + 1
-- 不过要注意,这种实现方式可能无法处理双位数字。 - Gary Greenmap
或者filter
呢?
reduce
就是为了这些操作而生的:
[1, 2, 3, 5, 2, 8, 9, 2].reduce( (count, val)=>count+(item==val), 0);
就是这样!(如果在每次迭代中item==val
,那么1将会被添加到累加器count
中,因为true
将解析为1
)。function countInArray(arr, val) {
return arr.reduce((count,item)=>count+(item==val),0)
}
Array.prototype.count = function(val) {
return this.reduce((count,item)=>count+(item==val),0)
}
我是js数组reduce函数的粉丝。
const myArray =[1, 2, 3, 5, 2, 8, 9, 2];
const count = myArray.reduce((count, num) => num === 2 ? count + 1 : count, 0)
事实上,如果您想要更加高级的操作,您可以在数组原型上创建一个计数函数,以便于重用。
Array.prototype.count = function(filterMethod) {
return this.reduce((count, item) => filterMethod(item)? count + 1 : count, 0);
}
const myArray =[1, 2, 3, 5, 2, 8, 9, 2]
const count = myArray.count(x => x==2)
array.reduce(function(total,x){return x==value? : total+1 : total}, 0)
。 - ninjagecko[...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
- indigoconst count = (list) => list.filter((x) => x == 2).length
。然后,通过调用count(list)
使用它,其中列表是数字数组。您还可以执行const count = (list) => list.filter((x) => x.someProp === 'crazyValue').length
来计算包含在对象数组中的crazyValue实例数。请注意,它必须与属性完全匹配。 - agm1984