在Javascript数组中将相似的项相加

3
我将尝试循环遍历一个数组,以便对其进行分组并计算总数。
例如:
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];

我将首先查看“Cats”,然后将所有猫的值相加,然后为其他唯一类别重复此过程。
最终输出如下:
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);

1
与论坛网站不同,我们在 [so] 上不使用“谢谢”、“感激任何帮助”或签名。请参阅Should 'Hi', 'thanks,' taglines, and salutations be removed from posts? - rene
1
“farm” 不是一个有效的……任何东西? - adeneo
@adeneo 是的,我认为它是 ['Dogs', 5] :) - Pointy
1
@Pointy - 在代码中使用了push(),所以数组的表示方式只是一个打字错误,在实际代码中是正确的。 - adeneo
1
我认为发布的代码中一个关键问题是“x”迭代变量没有被初始化。 - Pointy
显示剩余3条评论
6个回答

6
使用一个对象将类似的动物加在一起。
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;
}

示例

接下来,创建列表就很容易了。

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

FIDDLE


2
你缺少一个外层括号:
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];

你所拥有的最终会变得与之前一样。
var farm = ['Dogs', 5];

逗号运算符有一些奇怪的行为,尤其是在var语句中,因为,也用于分隔单独变量的声明和初始化。

1

我可能会稍微有所不同:

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开始创建一个新的计数器。这样可以处理任何重复项。

1
我看到了三个问题:
  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 = []; farm.push(['猫', 3], ['猫', 4], ['老鼠', 2], ['狗', 5]);

    var animalCounter = function (array) { var list = '', animalCounters = {}; for (var i = 0; i < array.length; i++) { var animalID = array[i][0]; var animalCount = array[i][1]; animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount; }

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

    } animalCounter(farm);

在这段代码中,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 加到 animalCount 中。这是必要的,因为如果在将 animalCounters[animalID] 设置为任何值之前尝试将其添加到 animalCount 中,那么加法将无法正常工作。


1
只是为了好玩...(不是非常实用)
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 数组减少到平坦数组,其中每个奇数索引是对应前一个元素的"count" - 注意检查偶数索引中的 "key" 是否已经存在于数组中 (如果是,则相应地更新其计数)slice 调用只是为了确保原始数组保持不变。 这会导致一个看起来像这样的数组:

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

这个新数组再次被缩减,这一次将每个元素连接成一个字符串——格式取决于当前迭代是否具有奇数或偶数索引。 Array.reduce是其中一种在旧浏览器中不受支持的函数(如果这很重要),但在MDN网站上有一个polyfill可用。

1
当您访问某种动物数量时,您犯了一个简单的错误:
animalCount += array[x][0] - 1; 

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

另外,第一个for循环:for(var i; i<array.length; i++){ ...即使已经计算过,也会循环遍历所有数组槽,所以猫会被计算两次,因此猫的数量将从7增加到14。

您需要创建一个数组的副本并删除已经计数的槽位。棘手的部分是通过值复制数组,以便对Temp的任何更改都不会更改farm (请参见复制数组):

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接