2

I am looking for a way to consolidate a collection of objects in javascript. For example I have a collection:

inventory = [ 
    {count: 1, type: "Apple"},
    {count: 2, type: "Orange"},
    {count: 1, type: "Berry"},
    {count: 2, type: "Orange"},
    {count: 3, type: "Berry"}
]

What I want to end up with is:

   inventory = [
        {count: 1, type: "Apple"},
        {count: 4, type: "Orange"},
        {count: 4, type: "Berry"}
     ]

Is there an elegant way to do this that doesn't involve getting a list of types, searching through my collection for those types, summing the values, and making a new array with the sums?

2
  • where are you getting this information? via a database? perhaps it would be better to aggregate natively rather than in JS. Commented Nov 13, 2013 at 5:55
  • I am getting this data through running regex on a list of strings, so unfortunately unable to aggregate before I get to the JS Commented Nov 13, 2013 at 5:59

3 Answers 3

3

It is not overly pretty, but this will do it. It creates both a dictionary of item types/counts, and a list of the final sums. inventoryDict is used to easily find the existing count, while summedInventory holds the final list of summed items.

var inventory = [ /* ... */ ];
var summedInventory = [];
var inventoryDict = {};

for (var i = 0; i < inventory.length; i++) {
    var item = inventory[i];
    if (!(item.type in inventoryDict)) {
        inventoryDict[item.type] = {type: item.type, count: 0};
        summedInventory.push(inventoryDict[item.type]);
    }
    inventoryDict[item.type].count += item.count;
}

This is is assuming that you do not want to mutate the inventory items in place - the loop can be slightly simplified if you do not mind mutating the items.

To avoid the intermediate variable and do it in a more functional manner, you could use Array.reduce:

var newInventory = inventory.reduce(function(acc, item) {
    var summedInventory = acc[0], inventoryDict = acc[1];

    if (!(item.type in inventoryDict)) {
        inventoryDict[item.type] = {type: item.type, count: 0};
        summedInventory.push(inventoryDict[item.type]);
    }

    inventoryDict[item.type].count += item.count;
    return acc;
}, [[], {}])[0];
Sign up to request clarification or add additional context in comments.

4 Comments

Did you test this? I thought the format would be item['type'] and item['count']
+1 for reduce solution which is a bit more intuitive IMO, albeit not as compatible.
@Zeaklous JavaScript allows you to use either obj.name or obj['name']. They are equivalent. When the property you are looking up is a valid identifier and known at the time of writing, the preferred option is usually obj.name. The other variant is useful for keys only known at run time, or for keys that are not valid identifiers: keyName = "hello"; obj[keyName] and obj['name with spaces'];
Right, right. Had to brush up on my jQuery formatting. All is good!
1

My solution is this:

inventory = [ 
    {count: 1, type: "Apple"},
    {count: 2, type: "Orange"},
    {count: 1, type: "Berry"},
    {count: 2, type: "Orange"},
    {count: 3, type: "Berry"}
];

result = {};
inventory.map(function(item) {
    console.log(item);
    var count = result[item.type] || 0;
    result[item.type] = item.count + count;
});

inventory = [];

for (property in result) {
    inventory.push({count: result[property], type: property});
}

console.log(inventory);

See this jsfiddle.

Comments

1

Here's a relatively slim way of doing it using Javascript's Array.reduce:

var reduced = inventory
    .reduce(function(sum,current) {
        var found = false
        sum.forEach(function(row,i) {
            if (row.type === current.type) {
                sum[i].count += current.count
                found = true;
            }
        })
        if (found === false) sum.push(current)
        return sum
    }, [])

console.log(reduced)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.