【发布时间】:2011-09-01 12:45:56
【问题描述】:
我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道数组中有多少个2。
在 JavaScript 中不使用 for 循环的最优雅的方法是什么?
【问题讨论】:
标签: javascript
我有一个数组:
[1, 2, 3, 5, 2, 8, 9, 2]
我想知道数组中有多少个2。
在 JavaScript 中不使用 for 循环的最优雅的方法是什么?
【问题讨论】:
标签: javascript
[这个答案有点过时:阅读编辑]
向你的朋友问好:map and filter and reduce and forEach and every etc.
(我只是偶尔在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)
(如果您需要优化这段特定的代码,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,因为像 '4'==4 这样的数字在 javascript 中......因此称它为.countEq 或.countNonstrict 强调它使用== 运算符。)
警告: 在原型上定义一个通用名称应该小心。如果你控制你的代码很好,但如果每个人都想声明他们自己的
[].count函数,特别是如果他们的行为不同的话,那就不好了。你可能会问自己“但.count(query)听起来确实非常完美和规范”......但考虑一下也许你可以做类似[].count(x=> someExpr of x)的事情。在这种情况下,您可以定义诸如countIn(query, container)(在myModuleName.countIn下)之类的函数,或者其他东西,或者[].myModuleName_count()。
还可以考虑使用您自己的多集数据结构(例如,像 python 的 'collections.Counter')以避免一开始就进行计数。这适用于 [].filter(x=> x==???).length 形式的完全匹配(最坏情况 O(N) 到 O(1)),修改后将加快对 [].filter(x=> x==???).length 形式的查询987654347@(大约乘以 #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) 内存),因为它几乎当然会生成一个中间数组,然后计算该中间数组的元素。
【讨论】:
array.reduce(function(total,x){return x==value? : total+1 : total}, 0)
[...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
const count = (list) => list.filter((x) => x == 2).length。然后通过调用 count(list) 来使用它,其中 list 是一个数字数组。您还可以使用const count = (list) => list.filter((x) => x.someProp === 'crazyValue').length 来计算对象数组中的 crazyValue 实例。请注意,它与属性完全匹配。
现代 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++;
}
【讨论】:
const count = countItems(array, 2);,实现细节可以在里面讨论。
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
【讨论】:
这是一种 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) 比counts[el] 更安全,因为虽然此处不适用,但零被视为假,因此如果counts[el] = 0,则counts[el] == false
我能想到的最奇怪的方法是:
(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
*/
你也可以重复调用indexOf,如果它作为一个数组方法可用,并且每次移动搜索指针。
这不会创建新数组,并且循环比 forEach 或过滤器快。
如果您有 100 万个会员可供查看,那可能会有所作为。
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——不过要注意这一点,否则你的实现不会很好地处理两位数。
我是 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 原型上创建一个计数函数。然后你可以重复使用它。
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)
【讨论】:
大多数使用数组函数(如过滤器)发布的解决方案是不完整的,因为它们没有参数化。
这里有一个解决方案,可以在运行时设置要计数的元素。
function elementsCount(elementToFind, total, number){
return total += number==elementToFind;
}
var ar = [1, 2, 3, 5, 2, 8, 9, 2];
var elementToFind=2;
var result = ar.reduce(elementsCount.bind(this, elementToFind), 0);
这种方法的优点是可以轻松更改函数以计算例如大于 X 的元素数。
你也可以内联声明reduce函数
var ar = [1, 2, 3, 5, 2, 8, 9, 2];
var elementToFind=2;
var result = ar.reduce(function (elementToFind, total, number){
return total += number==elementToFind;
}.bind(this, elementToFind), 0);
【讨论】:
var elementToFind=2; ... function (elementToFind, total, number){ return total += number==elementToFind; }.bind(this, elementToFind) ... 更难阅读,并且与 ... (acc, x) => acc += number == 2... 相比没有任何优势。我喜欢你使用+= 而不是acc + (number == 2)。感觉就像一个毫无根据的语法 HACK。
说真的,你为什么需要map 或filter 呢?
reduce 就是为这些操作“诞生”的:
[1, 2, 3, 5, 2, 8, 9, 2].reduce( (count,2)=>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)
}
【讨论】:
最好把它包装成函数:
let countNumber = (array,specificNumber) => {
return array.filter(n => n == specificNumber).length
}
countNumber([1,2,3,4,5],3) // returns 1
【讨论】:
我相信您正在寻找的是功能方法
const arr = ['a', 'a', 'b', 'g', 'a', 'e'];
const count = arr.filter(elem => elem === 'a').length;
console.log(count); // Prints 3
elem === 'a' 是条件,用你自己的替换它。
【讨论】:
count = arr.filter(elem => elem === 'a').length 或 count = arr.filter(elem => {return elem === 'a'}).length
Array.prototype.count = function (v) {
var c = 0;
for (let i = 0; i < this.length; i++) {
if(this[i] === v){
c++;
}
}
return c;
}
var arr = [1, 2, 3, 5, 2, 8, 9, 2];
console.log(arr.count(2)); //3
【讨论】:
我用这个:
function countElement(array, element) {
let tot = 0;
for(var el of array) {
if(el == element) {
tot++;
}
}
return tot;
}
var arr = ["a", "b", "a", "c", "d", "a", "e", "f", "a"];
console.log(countElement(arr, "a")); // 4
【讨论】:
递归求解
function count(arr, value) {
if (arr.length === 1) {
return arr[0] === value ? 1 : 0;
} else {
return (arr.shift() === value ? 1 : 0) + count(arr, value);
}
}
count([1,2,2,3,4,5,2], 2); // 3
【讨论】:
filter、reduce 或简单的forLoop 更复杂的方法,而且在性能方面也更昂贵,但仍然是递归的好方法。我唯一的改变是:我只是认为最好创建一个函数并在其中添加一个过滤器来复制数组并避免原始数组的突变,然后使用递归作为内部函数。
var arrayCount = [1,2,3,2,5,6,2,8];
var co = 0;
function findElement(){
arrayCount.find(function(value, index) {
if(value == 2)
co++;
});
console.log( 'found' + ' ' + co + ' element with value 2');
}
我会这样做:
var arrayCount = [1,2,3,4,5,6,7,8];
function countarr(){
var dd = 0;
arrayCount.forEach( function(s){
dd++;
});
console.log(dd);
}
【讨论】:
在核心级别文件中为 Array 类创建一个新方法,并在整个项目中使用它。
// say in app.js
Array.prototype.occurrence = function(val) {
return this.filter(e => e === val).length;
}
在项目中的任何地方使用它 -
[1, 2, 4, 5, 2, 7, 2, 9].occurrence(2);
// above line returns 3
【讨论】:
这是 javascript 中的单行代码。
(v === 2),返回一个由 1 和 0 组成的数组。[1, 2, 3, 5, 2, 8, 9, 2]
.map(function(v) {
return v === 2 ? 1 : 0;
})
.reduce((a, b) => a + b, 0);
结果是3。
【讨论】:
取决于你想如何运行它:
const reduced = (array, val) => { // self explanatory
return array.filter((element) => element === val).length;
}
console.log(reduced([1, 2, 3, 5, 2, 8, 9, 2], 2));
// 3
const reducer = (array) => { // array to set > set.forEach > map.set
const count = new Map();
const values = new Set(array);
values.forEach((element)=> {
count.set(element, array.filter((arrayElement) => arrayElement === element).length);
});
return count;
}
console.log(reducer([1, 2, 3, 5, 2, 8, 9, 2]));
// Map(6) {1 => 1, 2 => 3, 3 => 1, 5 => 1, 8 => 1, …}
【讨论】:
使用RegExp的另一种方法
const list = [1, 2, 3, 5, 2, 8, 9, 2]
const d = 2;
const counter = (`${list.join()},`.match(new RegExp(`${d}\\,`, 'g')) || []).length
console.log(counter)
步骤如下
【讨论】:
您可以使用内置函数Array.filter()
array.filter(x => x === element).length;
var arr = [1, 2, 3, 5, 2, 8, 9, 2];
// Count how many 2 there are in arr
var count = arr.filter(x => x === 2).length;
console.log(count);
【讨论】:
您可以在 JavaScript 数组中使用长度属性:
var myarray = [];
var count = myarray.length;//return 0
myarray = [1,2];
count = myarray.length;//return 2
【讨论】:
@{
/**/
var x = from z in Model.ListOfFaculty
select z;
}
@foreach (var c in x)
{
<div class="row">
<div class="col-lg-3">
<label>FacultyName :@c.Name </label>
</div>
<div class="col-lg-3">
<label>
Count :@{
var b = from v in Model.ListOfDepartment
where (v.Faculty_id == c.ID)
select v;
}
@b.Count()
</label>
</div>
</div>
}
</div>
【讨论】: