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

新手上路,请多包涵

我有一个数组:

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

我想知道数组中有多少 2

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

原文由 Leem 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 491
2 个回答

很简单:

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

原文由 Thor Jacobsen 发布,翻译遵循 CC BY-SA 3.0 许可协议

[ 这个答案有点过时:阅读编辑,在 javascript 中“相等”的概念是模棱两可的]

Say hello to your friends: 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)

或者正如评论者善意指出的那样:

 [....].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()

还可以考虑使用您自己的多集数据结构(例如像 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) 内存),因为它几乎当然会生成一个中间数组,然后计算该中间数组的元素。

原文由 ninjagecko 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题