【问题标题】:Adding Similar Items Together in a Javascript Array在 Javascript 数组中一起添加相似的项目
【发布时间】:2014-01-19 04:33:38
【问题描述】:

我正在尝试遍历一个数组,以便对总数进行分组和计数。

例如:

var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];

我想先查看“Cats”,然后将所有 cat 值相加,然后对其他唯一类别重复此过程。

最终的输出是:

Cats (7)
Mice (2)
Dogs (5)

目前,我正在尝试以这种方式完成它,但显然我在某个地方犯了一个新手错误。

var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function(array){
    var list = '';
    for(var i = 0; i<array.length; i++){
        var animalID = array[i][0];
        var animalCount = 0;
        for (var x; x<array.length; x++){
            if(animalID == array[x][0]){
                animalCount += array[x][0] - 1;
            }
            list += animalID + " (" + animalCount + ")\n";
        }

    }
    alert(list);
}
animalCounter(farm);

【问题讨论】:

  • 与论坛网站不同,我们不使用“谢谢”、“感谢任何帮助”或Stack Overflow 上的签名。见Should 'Hi', 'thanks,' taglines, and salutations be removed from posts?
  • farm 无效.... 什么?
  • @adeneo 是的,我认为是['Dogs', 5] :)
  • @Pointy - 在代码中它使用了 push(),所以这只是数组呈现方式的一个错字,在实际代码中是正确的。
  • 另外我认为发布代码的一个关键问题是“x”迭代变量没有初始化。

标签: javascript arrays sorting multidimensional-array


【解决方案1】:

当您访问某种动物的数量时,您犯了一个简单的错误:

animalCount += array[x][0] - 1; 

farm[x][0] 将始终返回动物的名称,它是一个字符串,因此当尝试从中减去 1 时,它将导致 NaN(不是数字)。

也是第一个 for 循环:for(var i; i&lt;array.length; i++){ ... 循环遍历所有数组槽,即使它们已经被计算在内,所以猫会被计算两次,所以猫会被计算为 14 而不是 7。

您需要创建一个数组副本并删除已计算的插槽。棘手的部分是按值复制数组,因此对 Temp 的任何更改都不会更改农场(请参阅Copying Arrays):

var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
function countAnimals(array) {
    var Temp = [];
    var d = 0;
    //This while loop copies the array
    while (d < farm.length) {
        var s = array[d].toString();
        Temp.push(s.split(","));
        d++;
    }
    var list = "";
    var done = 0;
    while (done < array.length) {
        if(Temp[0][1] == "Done") {
            Temp.shift();
        } else {
            var animalID = Temp[0][0];
            var count = parseFloat(Temp[0][1]);
            Temp.shift();
            var i = 0;
            while (i < Temp.length) {
                if(Temp[i][0] == animalID) {
                    count = count + parseFloat(Temp[i][1]);
                    Temp[i][1] = "Done";
                }
                i++;
            }
            list = list + "\n" + animalID + "("+count+")";
        }
        done++;
    }
    alert(list);
}
countAnimals(farm);

【讨论】:

    【解决方案2】:

    只是为了好玩...... (不是很实用)

    var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
    
    farm.reduce(function(a, b, i){
        var r = (i==1 ? a.slice() : a), 
            j = r.indexOf(b[0]);
        if(j >= 0){ 
            r[j+1] += b[1];
            return r;
        } else return r.concat(b); 
    }).reduce(function(a, b, i){
        return i%2 ? a+' ('+b+')' : a+'\n'+b;
    });
    

    粗略解释:

    遍历farm 的每个元素,将2D 数组缩减为一个平面数组,其中每个odd 索引都是" count" 对应于前一个元素 - 注意检查 even 索引中的 "key" 是否已经存在于数组(在这种情况下分别更新它的计数)slice 调用只是为了确保原始数组保持不变。这导致一个数组看起来像:

        ["Cats", 7, "Mice", 2, "Dogs", 5]
    

    这个新数组再次被缩减,这次将每个元素连接成一个字符串 - 格式化取决于当前迭代是否具有奇数或偶数索引。

    Array.reduce 是旧版浏览器不支持的功能之一(如果这很重要),但 MDN 网站上有一个 polyfill

    【讨论】:

      【解决方案3】:

      我看到三个问题:

      1. 将 animalCounter 设置为动物的数量 - 新的动物数量会替换可能已经存储在 animalCounter 中的任何内容,因此这里没有添加任何内容
      2. 在循环内创建 animalCounter 变量 - 如果 var animalCount 在循环内,那么实际上数组的每个元素都有一个全新的变量
      3. 对所有类型的动物使用一个变量

      试试这个:

      var farm = [];
      farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
      
      var animalCounter = function (array) {
          var list = '',
              catsCount = 0,
              miceCount = 0,
              dogsCount = 0;
          for (var i = 0; i < array.length; i++) {
              var animalID = array[i][0];
              var animalCount = array[i][1];
              if (animalID === 'Cats') {
                  catsCount += animalCount;
              } else if (animalID === 'Mice') {
                  miceCount += animalCount;
              } else if (animalID === 'Dogs') {
                  dogsCount += animalCount;
              }
          }
          list = 'Cats(' + catsCount + ') Mice(' + miceCount + ') Dogs(' + dogsCount + ')';
          alert(list);
      }
      animalCounter(farm);
      

      每种动物都有单独的变量,数组中的值被添加到正确的计数器变量中。

      或者,对于更有条理的解决方案:

      1. var 农场 = []; farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['狗', 5]);

        var animalCounter = 函数(数组){ 变量列表 = '', 动物计数器 = {}; for (var i = 0; i

        for (var id in animalCounters) {
            list += id + " (" + animalCounters[id] + ")\n";
        }
        alert(list);
        

        } 动物计数器(农场);

      在这段代码中,animalCounters 变量是一个对象。 JavaScript 对象的行为类似于关联数组,这让我们可以使用动物 ID 字符串作为整数的“键”,即动物总和。每种类型的动物都是 animalCounters 对象的一个​​属性,以该类型动物的总和作为其值。

      我在这里使用了一些有点晦涩的符号,所以我会解释一下。

      animalCounters[animalID]
      

      这只是引用对象属性的另一种方法。在 JavaScript 中,animalCounters.CatsanimalCounters["Cats"] 访问相同的东西。但是,如果您不确定动物的类型是 Cats,则需要将“Cats”(或任何其他种类的动物)放在变量中。 animalCounters["Cats"] 表示法需要一个字符串,所以你可以这样说,它会起作用:

      var animalID = "Dogs";
      alert(animalCounters[animalID);// returns animalCounters.Dogs
      
      
      
      
      animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount;
      

      这里,(animalCounters[animalID] || 0) 表示如果 animalCounters[animalID] 已经有一个值,将该值添加到animalCount否则0 添加到@ 987654333@。这是必要的,因为如果您在将animalCounters[animalID] 设置为任何值之前尝试将animalCounters[animalID] 添加到animalCount,则添加将无法正常工作。

      【讨论】:

        【解决方案4】:

        我可能会做一些不同的事情:

        var farm = [];
        farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
        
        var animalCounter = function(array){
          var animalObject = {};
          for(var i = 0; i<array.length; i++){
            var animalID = array[i][0];
            var animalCount = array[i][1];
            if(animalObject[animalID]) {
              animalObject[animalID] += animalCount;
            } else {
              animalObject[animalID] = animalCount;
            }
          }
          return animalObject;
        }
        

        第一个函数animalCounter 创建一个将动物名称映射到数组中的数字的对象。因此,对于您的示例,它将返回一个如下所示的对象:

        { 'Cats': 7, 'Mice': 2, 'Dogs': 5 }
        

        从那里,一旦你有了对象,创建一个字符串以你喜欢的任何格式输出这个数据是微不足道的:

        var counter = animalCounter(farm);
        var list = '';
        for(var key in counter) {
          list += key + ': ' + counter[key] + ', ';
        }
        console.log(list); //=> Cats: 7, Mice: 2, Dogs: 5,
        

        您的初始函数不起作用的原因是因为它没有考虑到您的数组中可能有多个相同动物的实例。如果您的原始数组是[['Cats', 7], ['Mice', 2], ['Dogs', 5]],那会很好,但是因为您的Cats 条目被拆分为['Cats', 4], ['Cats', 3],您的原始代码将其视为两个不同的动物。在我的函数中注意:

        if(animalObject[animalID]) {
          animalObject[animalID] += animalCount;
        } else {
          animalObject[animalID] = animalCount;
        }
        

        代码检查动物是否已存储在新对象中,如果是,则增加计数,而不是从 0 开始创建新计数器。这样它会处理任何重复项。

        【讨论】:

          【解决方案5】:

          使用一个对象将相似的动物添加到一起

          var farm = [];
          farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
          
          var o = {};
          
          for (var i=farm.length; i--;) {
              var key = farm[i][0],
                  val = farm[i][1];
          
              o[key] = key in o ? o[key] + val : val;
          }
          

          FIDDLE

          那么创建列表就很简单了

          for (var key in o) {
              list += key + " (" + o[key] + ")\n";
          }
          

          FIDDLE

          【讨论】:

            【解决方案6】:

            你少了一层括号:

            var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
            

            你所拥有的最终会是一样的

            var farm = ['Dogs', 5];
            

            逗号操作符会做一些奇怪的事情,尤其是var 语句中,因为, 也将单独的变量声明和初始化分开。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-10-18
              • 1970-01-01
              • 2020-06-19
              • 2019-10-27
              • 1970-01-01
              • 2020-03-05
              • 1970-01-01
              • 2019-08-01
              相关资源
              最近更新 更多