简体   繁体   English

在Javascript数组中一起添加类似项

[英]Adding Similar Items Together in a Javascript Array

I'm trying to loop through an array in order to group and count totals. 我正在尝试遍历一个数组,以便对总数进行分组和计数。

For example: 例如:

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

I would like to go through and first see 'Cats', then add all the cat values up, then repeat the process for other unique categories. 我想通过并首先看到'猫',然后添加所有猫值,然后重复其他独特类别的过程。

The final output would be: 最终的输出是:

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

Currently, I'm trying to accomplish it this way, but I'm obviously making a rookie mistake somewhere. 目前,我正试图以这种方式实现它,但我显然在某个地方犯了一个菜鸟错误。

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

use an object to add similar animals together 使用一个物体将类似的动物加在一起

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 小提琴

Then it's easy to create the list 然后很容易创建列表

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

FIDDLE 小提琴

You're missing an outer layer of brackets: 你错过了一个外层括号:

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

What you had will end up being the same as 你拥有的东西最终会和你一样

var farm = ['Dogs', 5];

The comma operator does strange things, especially in a var statement because , also separates individual variable declarations and initializations. 逗号操作符做奇怪的事情, 尤其是var语句,因为,也会把个体变量声明和初始化。

Just for funsies... (not very practical) 只为了傻瓜... (不太实用)

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

Rough explanation: 粗略的解释:

Iterate over each element of farm reducing the 2D array to a flat array where every odd index is the "count" that corresponds to the previous element - taking note to check if the "key" in the even index already exists in the array (in which case update it's count respectively) . 迭代farm每个元素,将2D数组缩减为平面数组,其中每个奇数索引都是与前一个元素对应的“计数” - 注意检查偶数索引中的“键”是否已存在于数组中(在哪个案例分别更新了它的数量) The slice call is in there just to make sure that the original array is left unmodified. slice调用就在那里,以确保原始数组保持不变。 This results in an array looking like: 这导致数组看起来像:

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

This new array is reduced once more, this time concatenating each element into a single string - formatting dependent on whether or not the current iteration has an odd or even index. 这个新数组再次减少,这次将每个元素连接成一个字符串 - 格式取决于当前迭代是否具有奇数或偶数索引。

Array.reduce is one of those functions that isn't supported in older browsers (if that is important) but there's a polyfill available on the MDN site. Array.reduce是未在旧的浏览器支持(如果这很重要),但有一个这些功能一个填充工具在MDN网站上提供。

I'd probably do it a bit differently: 我可能会做的有点不同:

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

The first function, animalCounter , creates an object that maps animal names to the numbers in the array. 第一个函数animalCounter创建一个对象,将动物名称映射到数组中的数字。 So for your example, it will return an object that looks like this: 因此,对于您的示例,它将返回如下所示的对象:

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

From there, once you have the object, it's trivial to create a string to output this data in whatever format you like: 从那里开始,一旦你拥有了这个对象,创建一个字符串就能以你喜欢的任何格式输出这些数据是微不足道的:

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

The reason your initial function didn't work is because it didn't take into account that there might be more than one instance of the same animal in your array. 你的初始函数不起作用的原因是因为它没有考虑到你的数组中可能有多个同一动物的实例。 If your original array was [['Cats', 7], ['Mice', 2], ['Dogs', 5]] it would have been fine, but because your Cats entry was split into ['Cats', 4], ['Cats', 3] , your original code saw that as two distinct animals. 如果你的原始数组是[['Cats', 7], ['Mice', 2], ['Dogs', 5]]它会没问题,但因为你的Cats条目分为['Cats', 4], ['Cats', 3] ,你的原始代码看到了两个不同的动物。 Notice in my function: 注意我的功能:

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

The code checks to see if the animal is already stored in the new object, and if it is, increments the count, rather than creating a new counter from 0. This way it deals with any duplicates. 代码检查动物是否已经存储在新对象中,如果是,则递增计数,而不是从0创建新计数器。这样它就可以处理任何重复项。

I see three problems: 我看到三个问题:

  1. setting animalCounter equal to the number of animals - the new number of animals replaces whatever might already have been stored in animalCounter, so nothing is added up here 将animalCounter设置为等于动物的数量 - 新的动物数量取代了已经存储在animalCounter中的任何动物,因此这里没有添加任何内容
  2. creating the animalCounter variable within the loop - if var animalCount is inside the loop, then you actually have a completely new variable for each element of the array 在循环中创建animalCounter变量 - 如果var animalCount在循环内,那么你实际上为数组的每个元素都有一个全新的变量
  3. using a single variable for all the types of animals 对所有类型的动物使用单个变量

Instead, try this: 相反,试试这个:

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

There are separate variables for each type of animal, and the value in the array is added onto the correct counter variable. 每种动物都有单独的变量,数组中的值被添加到正确的计数器变量上。

Or, for a more organized solution: 或者,对于更有条理的解决方案:

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

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

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

    } animalCounter(farm); } animalCounter(farm);

In this code, the animalCounters variable is an object. 在此代码中,animalCounters变量是一个对象。 JavaScript objects act like associative arrays, which lets us use the animal ID string as a "key" to an integer that is the animal sum. JavaScript对象就像关联数组一样,它允许我们使用动物ID字符串作为动物总和的整数的“关键字”。 Each type of animal is a property of the animalCounters object, with the sum for that type of animal as its value. 每种类型的动物都是animalCounters对象的属性,其中该类动物的总和为其值。

I used some slightly obscure notation here, so I'll explain. 我在这里使用了一些略显晦涩的符号,所以我会解释一下。

animalCounters[animalID]

This is just a different method of referring to properties of an object. 这只是引用对象属性的另一种方法。 In JavaScript, animalCounters.Cats and animalCounters["Cats"] access the same thing. 在JavaScript中, animalCounters.CatsanimalCounters["Cats"]访问同样的东西。 But, if you don't know for sure that the type of animal will be Cats, you need "Cats" (or whatever other kind of animal) to be in a variable. 但是,如果你不确定动物的类型是猫,你需要“猫”(或任何其他种类的动物)变量。 The animalCounters["Cats"] notation takes a string, so you can say this and it will work: animalCounters["Cats"]表示法带有一个字符串,所以你可以说这个并且它会起作用:

var animalID = "Dogs";
alert(animalCounters[animalID);// returns animalCounters.Dogs




animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount;

Here, the (animalCounters[animalID] || 0) is saying that if animalCounters[animalID] already has a value, add that value to animalCount , otherwise add 0 to animalCount . 这里, (animalCounters[animalID] || 0)表示如果 animalCounters[animalID]已经有值,则将该值添加到animalCount否则0添加到animalCount This is necessary because if you try to add animalCounters[animalID] to animalCount before animalCounters[animalID] has been set to anything, the addition won't work right. 这是必要的,因为如果你尝试在animalCounters[animalID]被设置为任何东西之前将animalCounters[animalID]添加到animalCount ,则添加将无法正常工作。

When you access the amount of animals of a certain kind you made a simple mistake: 当你访问某种动物的数量时,你犯了一个简单的错误:

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

farm[x][0] will always return the animal's name which is a string, so when trying to subtract 1 from it it will result in NaN (Not a Number). farm[x][0]将始终返回动物的名称,这是一个字符串,因此当尝试从中减去1时,它将导致NaN (非数字)。

Also the first for loop: for(var i; i<array.length; i++){ ... cycles through all the array slots even if they were already counted, so cats would be counted twice so instead of cats counted as 7 they would amount to 14. 也是循环的第一个: for(var i; i<array.length; i++){ ...循环遍历所有数组插槽,即使它们已被计数,因此猫将被计数两次,因此猫而不是计为7将达到14。

You need to create a copy of array and take off the slots already counted. 您需要创建一个数组副本并取消已经计数的插槽。 The tricky part is copying the array by value and so that any changes to Temp won't change farm (see Copying Arrays ): 棘手的部分是按值复制数组,因此对Temp的任何更改都不会更改服务器场(请参阅复制数组 ):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM