【问题标题】:How to count certain elements in array?如何计算数组中的某些元素?
【发布时间】:2011-09-01 12:45:56
【问题描述】:

我有一个数组:

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

我想知道数组中有多少个2

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

【问题讨论】:

    标签: javascript


    【解决方案1】:

    [这个答案有点过时:阅读编辑]

    向你的朋友问好: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=&gt; someExpr of x) 的事情。在这种情况下,您可以定义诸如countIn(query, container)(在myModuleName.countIn 下)之类的函数,或者其他东西,或者[].myModuleName_count()

    还可以考虑使用您自己的多集数据结构(例如,像 python 的 'collections.Counter')以避免一开始就进行计数。这适用于 [].filter(x=&gt; x==???).length 形式的完全匹配(最坏情况 O(N)O(1)),修改后将加快对 [].filter(x=&gt; 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 =&gt; x==2).length。如果您关心性能,请注意虽然这与 for 循环(O(N) 时间)的性能渐近相同,但它可能需要 O(N) 额外内存(而不是 O(1) 内存),因为它几乎当然会生成一个中间数组,然后计算该中间数组的元素。

    【讨论】:

    • 这是一个很好的 FP 解决方案,唯一的“问题”(与大多数情况无关)它创建了一个中间数组。
    • @tokland:如果这是一个问题,你可以这样做array.reduce(function(total,x){return x==value? : total+1 : total}, 0)
    • @ninjagecko 三元运算符不应该只有一个冒号吗? [...].reduce(function(total,x){return x==2 ? total+1 : total}, 0)
    • @tokland 也许过滤器不会创建一个中间数组。一个好的优化编译器可以很容易地识别出只使用了数组的长度。也许当前的 JS 编译器都没有足够聪明的能力来做到这一点,但这并不重要。正如福勒所说,“如果有什么伤害,那就多做点”。通过编写糟糕的代码来避免编译器缺陷是短视的。如果编译器很糟糕,请修复编译器。 mlafeldt.github.io/blog/if-it-hurts-do-it-more-often
    • 我认为这是 2017 年的最佳解决方案:const count = (list) =&gt; list.filter((x) =&gt; x == 2).length。然后通过调用 count(list) 来使用它,其中 list 是一个数字数组。您还可以使用const count = (list) =&gt; list.filter((x) =&gt; x.someProp === 'crazyValue').length 来计算对象数组中的 crazyValue 实例。请注意,它与属性完全匹配。
    【解决方案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
    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 的检查,如本例所示。

    【讨论】:

    • 过滤器功能是否比使用es6 for of循环更高效?
    • @Niklas,我认为它是相同的(因为两者都必须检查所有元素,O(N)),但是,我想这取决于浏览器,也取决于元素的数量和计算机的数量适合高速缓存。所以,我想答案是:“这很复杂”:)
    【解决方案3】:

    很简单:

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

    【讨论】:

    • 不,我的意思是不用“for”循环
    • @Leem:为什么循环不好?在某些时候总是有循环。显然,您将创建一个隐藏循环的函数。这些“我不想为工作使用正确的工具”——请求对我来说从来没有多大意义。我们可以争论什么是最优雅的。例如。对我来说,对每个元素进行函数调用只是为了将其与值进行比较并不优雅。
    • 为笑:alert(eval('('+my_array.join('==2)+(')+'==2)')) jsfiddle.net/gaby_de_wilde/gujbmych
    • OP 可能认为循环不好,因为它是 5 行代码并且需要可变状态。稍后来阅读的开发人员将不得不花一些时间来检查它的作用,并且会从他们的任务中失去注意力。抽象要好得多:const count = countItems(array, 2);,实现细节可以在里面讨论。
    • 这不是正确的答案,因为该问题明确要求不使用循环。检查我不使用循环的解决方案。 stackoverflow.com/a/44743436/8211014
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

      • 巨大的矫枉过正。因为它为所有独特元素创建计数器。浪费存储空间和时间。
      • 请记住,lodash 总是会增加你的包大小,通常会非常显着。对于像计算数组项这样简单的事情,我不会使用 lodash。
      【解决方案6】:

      这是一种 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
      【解决方案7】:

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

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

      地点:

      • a 是数组
      • n 是要在数组中计数的数字

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

      【讨论】:

        【解决方案8】:

        不使用循环通常意味着将过程交给确实使用循环的方法。

        这是我们讨厌循环的编码人员可以以某种代价满足他的厌恶的一种方式:

        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——不过要注意这一点,否则你的实现不会很好地处理两位数。
        • [2, 22, 2] 怎么样?
        【解决方案9】:

        我是 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)
        

        【讨论】:

          【解决方案10】:

          大多数使用数组函数(如过滤器)发布的解决方案是不完整的,因为它们没有参数化。

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

          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) =&gt; acc += number == 2... 相比没有任何优势。我喜欢你使用+= 而不是acc + (number == 2)。感觉就像一个毫无根据的语法 HACK。
          【解决方案11】:

          说真的,你为什么需要mapfilter 呢? reduce 就是为这些操作“诞生”的:

          [1, 2, 3, 5, 2, 8, 9, 2].reduce( (count,2)=&gt;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)
          }
          

          【讨论】:

            【解决方案12】:

            最好把它包装成函数:

            let countNumber = (array,specificNumber) => {
                return array.filter(n => n == specificNumber).length
            }
            
            countNumber([1,2,3,4,5],3) // returns 1
            

            【讨论】:

              【解决方案13】:

              我相信您正在寻找的是功能方法

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

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

              【讨论】:

              • 它不会打印 3,而是 0。要修复它,您的第二行应该是 count = arr.filter(elem =&gt; elem === 'a').lengthcount = arr.filter(elem =&gt; {return elem === 'a'}).length
              【解决方案14】:

              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

              【讨论】:

                【解决方案15】:

                我用这个:

                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

                【讨论】:

                  【解决方案16】:

                  递归求解

                  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
                  

                  【讨论】:

                  • 这是否处理空数组?
                  • @AndrewGrimm 说得对。基本情况是 arr.length == 0
                  • 不错的解决方案!我试图使用递归来做一些事情只是为了练习,你的例子比我正在做的更优雅。这绝对是一种比使用filterreduce 或简单的forLoop 更复杂的方法,而且在性能方面也更昂贵,但仍然是递归的好方法。我唯一的改变是:我只是认为最好创建一个函数并在其中添加一个过滤器来复制数组并避免原始数组的突变,然后使用递归作为内部函数。
                  【解决方案17】:

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

                  【讨论】:

                    【解决方案18】:

                    在核心级别文件中为 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
                    

                    【讨论】:

                      【解决方案19】:

                      这是 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

                      【讨论】:

                        【解决方案20】:

                        取决于你想如何运行它:

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

                        【讨论】:

                          【解决方案21】:

                          使用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. 获取匹配项的长度

                          【讨论】:

                            【解决方案22】:

                            您可以使用内置函数Array.filter()

                            array.filter(x =&gt; 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);

                            【讨论】:

                              【解决方案23】:

                              您可以在 JavaScript 数组中使用长度属性:

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

                              【讨论】:

                              • 如果你先过滤它然后你可以使用长度。即array.filter(x => x === 2).length
                              【解决方案24】:
                                      @{
                                          /**/
                              
                                          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>
                              

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2017-02-17
                                • 2021-12-16
                                • 1970-01-01
                                • 1970-01-01
                                • 2021-06-10
                                • 2015-07-13
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多