如何计算数组中的某些元素?

我有一个数组:

[1, 2, 3, 5, 2, 8, 9, 2]

我想知道数组中有多少__abc0。

在JavaScript中不使用for循环的最优雅的方法是什么?

822747 次浏览

非常简单:

var count = 0;
for(var i = 0; i < array.length; ++i){
if(array[i] == 2)
count++;
}

(这个答案有点过时了:阅读编辑,在javascript中“相等”的概念是模糊的)

向你的朋友问好:mapfilterreduceforEachevery等等。

(我只是偶尔用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)

或者正如一位评论者好心指出的那样:

[....].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,因为像'4'==4这样的数字在javascript中…因此称它为.countEq.countNonstrict强调它使用==操作符。)

< p >注意: 在原型上定义一个通用名称时应该谨慎。如果你控制你的代码,这很好,但如果每个人都想声明自己的[].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(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的一次性一行程序),你现在可以把它写得更简洁:[...].filter(x => x==2).length。如果您关心性能,请注意,虽然这与for循环(O(N)时间)的性能渐近相同,但它可能需要O(N)额外的内存(而不是O(1)内存),因为它几乎肯定会生成一个中间数组,然后计算该中间数组的元素。

我能想到的最奇怪的方法是:

(a.length-(' '+a.join(' ')+' ').split(' '+n+' ').join(' ').match(/ /g).length)+1

地点:

  • 一个是数组
  • n是数组中要计数的数字

我的建议,使用while或for循环;-)

不使用循环通常意味着将进程交给使用循环的某个方法。

这里有一个讨厌循环的编码器可以以一定的代价来满足他的厌恶:

var a=[1, 2, 3, 5, 2, 8, 9, 2];


alert(String(a).replace(/[^2]+/g,'').length);




/*  returned value: (Number)
3
*/

如果indexOf作为数组方法可用,还可以重复调用indexOf,并每次移动搜索指针。

这不会创建一个新的数组,并且循环比forEach或过滤器更快。

如果你有一百万个会员可以查看,那就会有所不同。

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
*/

如果你使用的是lodash或下划线,_.countBy方法将提供一个由数组中的每个值作为键的聚合总数的对象。如果你只需要计算一个值,你可以把它变成一行代码:

_.countBy(['foo', 'foo', 'bar'])['foo']; // 2

这也适用于数字数组。示例中的一行代码是:

_.countBy([1, 2, 3, 5, 2, 8, 9, 2])[2]; // 3

递归解

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)的解决方案都是不完整的,因为它们没有参数化。

这里有一个解决方案,可以在运行时设置要计数的元素。

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 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);
}

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);

你可以在JavaScript数组中使用length属性:

var myarray = [];
var count = myarray.length;//return 0


myarray = [1,2];
count = myarray.length;//return 2

现代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
// 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

其中隐含return

在JS中总是使用三重等号:===进行比较,检查可空性时除外:if (something == null) {},因为它包括检查undefined,如果你只使用双等号,就像在这种情况下。

在核心级别文件中为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中的一行代码。

  1. 使用地图。在数组中找到匹配的值(v === 2),返回一个由1和0组成的数组。
  2. 使用减少。将该数组的所有值相加,得到所找到的总数。
[1, 2, 3, 5, 2, 8, 9, 2]
.map(function(v) {
return v === 2 ? 1 : 0;
})
.reduce((a, b) => a + b, 0);

结果是3

我相信你们在寻找的是功能方法

    const arr = ['a', 'a', 'b', 'g', 'a', 'e'];
const count = arr.filter(elem => elem === 'a').length;
console.log(count); // Prints 3

Elem === 'a'是条件,用你自己的替换它。

真的,为什么你需要mapfilter来做这个? reduce为这类操作而“诞生”:

[1, 2, 3, 5, 2, 8, 9, 2].reduce( (count,2)=>count+(item==val), 0);

就是这样!(如果在每次迭代中item==val,则累加器count将添加1,因为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原型上创建一个count函数。然后你就可以重复使用了。

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)

最好把它包装成函数:

let countNumber = (array,specificNumber) => {
return array.filter(n => n == specificNumber).length
}


countNumber([1,2,3,4,5],3) // returns 1

下面是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 ]
]

这取决于你想如何运行它:

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, …}

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

另一种方法使用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)

步骤如下

  1. 使用逗号记住在连接后添加',',这样当要匹配的值在数组的末尾时,就不会有不正确的值连接字符串
  2. 匹配数字和逗号组合的出现次数
  3. 获取匹配项的长度

你可以使用内置函数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);

我用这个:

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

一行程序功能

const countBy = (a,f)=>a.reduce((p,v,i,x)=>p+!!f(v,i,x), 0)
countBy([1,2,3,4,5], v=>v%2===0) // 2

有很多方法可以找到答案。我认为最简单的方法是使用es6中引入的数组过滤器方法。

function itemCount(array, item) {
return array.filter(element => element === item).length
}


const myArray = [1,3,5,7,1,2,3,4,5,1,9,0,1]
const items = itemCount(myArray, 1)
console.log(items)

我相信您可以使用JavaScript的新Set数组方法来拥有唯一的值。

例子:

var arr = [1, 2, 3, 5, 2, 8, 9, 2]
var set = new Set(arr);
 

console.log(set);

// 1,2,3,5,8,9。我们得到唯一的值作为输出。

一些更普通和现代的东西(2022年):

import {pipe, count} from 'iter-ops';


const arr = [1, 2, 3, 5, 2, 8, 9, 2];


const n = pipe(arr, count(a => a === 2)).first; //=> 3

这样做的好处是:

  1. 它不需要创建一个新的数组,所以它是内存高效的
  2. 对于任何IterableAsyncIterable都是一样的