0

I have an array of objects like this:

[
{
    item: 'abc',
    category: 'car',
    price: 1000
},
{
    item: 'abc',
    category: 'bike',
    price: 1000
},
{
    item: 'abc',
    category: 'car',
    price: 2000
},
{
    item: 'abc',
    category: 'car',
    price: 3000
},
{
    item: 'abc',
    category: 'bike',
    price: 500
},
{
    item: 'abc',
    category: 'plane',
    price: 9000
},

]

I want to create a new Array from above array which groups objects by category and has total price for that category as value for 'price' key:

[
{
    category: car,
    price: 6000
},
{
    category: bike,
    price: 1500
},
{
    category: plane,
    price: 9000
}

]

How can I do this in Javascript?

2
  • what have you done so far ? have you written any code for it ? Commented Apr 12, 2017 at 10:10
  • Have you done any research? Tasks like this are quite simple. Commented Apr 12, 2017 at 10:13

4 Answers 4

2

Basic approach is

  • create a map first with category as key and price as value
  • convert this map into an array

DEMO

var input = [
{
    item: 'abc',
    category: 'car',
    price: 1000
},
{
    item: 'abc',
    category: 'bike',
    price: 1000
},
{
    item: 'abc',
    category: 'car',
    price: 2000
},
{
    item: 'abc',
    category: 'car',
    price: 3000
},
{
    item: 'abc',
    category: 'bike',
    price: 500
},
{
    item: 'abc',
    category: 'plane',
    price: 9000
},
];

var map = {};
input.forEach( function ( item ){
  map [ item.category ] = map [ item.category ] || 0;
  map [ item.category ] += item.price;
});
var output = Object.keys( map ).map( function(key){ return { category: key, price : map[key] } });

console.log( output );

Sign up to request clarification or add additional context in comments.

4 Comments

Please stop answering such poor questions. They most likely have duplicates. Also, you're answer is a good one.
@evolutionxbox though I agree that there could be duplicate of this question, but Array question duplicates are very hard to find, even if they are about grouping of some kind. Looking for the duplicate as well.
just FYI it's giving wrong price sum for car object, it's giving 3k in console instead it should be 6k
Just update the for loop. input.forEach( function ( item ){ var obj = map [ item.category ]; if(obj == undefined) { map [ item.category ] = item.price; } else { obj = obj+item.price; map [ item.category ] = obj; } });
1

You could group it with a hash table and take it as reference for adding the values.

Main structure:

grouped = data.reduce(function (hash) {    // closure over hash
    return function (r, a) { /* ... */ };  // returns function for callback
}(Object.create(null)), []);               // call of IFFE with an empty object

The main idea is to use a variable hash outside of the global context, but usable inside of the callback for Array#reduce.

This works with an IIFE (Immediately Invoked Function Expression), which is a function which is directly after declaration invoked with an empty object.

This creates the variable hash with an empty object and returns the function for the callback.

Inside of the callback:

The callback works with two variables, r and a.

function (r, a) {
    // business logic
    return r;
}

At start, variable r gets the initialValue, of an empty array []. In this array, the result set is collected and returned for every iteration. This parameter is describes as accumulator.

Parameter a is the current value of the array. This is a single object, like

{
    item: 'abc',
    category: 'car',
    price: 1000
}

and only one object is being processed at the same time.

Basically this solution works with an object and it looks at the end like this

{
    car: {
        category: "car",
        price: 6000
    },
    bike: {
        category: "bike",
        price: 1500
    },
    plane: {
        category: "plane",
        price: 9000
    }
}

where the properties, like car or bike give the reference to the result array without the property car or bike.

At first, a check is made, if the category exists in the hash table. If not, then a new object is created with category and a price with zero, because the price property gets updated for every item within the same category. Then the new object reference gets pushed to the result array.

After the check, the property exist already or is just created, the price property gets an update with the actual value.

At last, the result set is returned for the next update iteration or as final result.

var data = [{ item: 'abc', category: 'car', price: '1000' }, { item: 'abc', category: 'bike', price: '1000' }, { item: 'abc', category: 'car', price: '2000' }, { item: 'abc', category: 'car', price: '3000' }, { item: 'abc', category: 'bike', price: '500' }, { item: 'abc', category: 'plane', price: '9000' }],
    grouped = data.reduce(function (hash) {
        return function (r, a) {
            if (!hash[a.category]) {
                hash[a.category] = { category: a.category, price: 0 };
                r.push(hash[a.category]);
            }
            hash[a.category].price += +a.price;
            return r;
        };
    }(Object.create(null)), []);

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

6 Comments

Is there any difference if we use just {} instead of Object.create(null) in this case? really nice approach +1!
@loelsonk, actually not, but if you like to group by some prototypes, like toString, then a really empty object would help.
@Nina Scholz: This works, but could you briefly explain the code? Also I had to do hash[a.category].price = parseInt(hash[a.category].price) + parseInt(a.price) instead of hash[a.category].price += a.price as it was taking the numbers as strings and concatenating them instead of numerically adding them.
@dk49, you could use an unary + for converting strings to numbers, like above.
@Nina Scholz: ok..but you still didn't explain the mechanism of this code. I understand what reduce method does, but not sure why an inner returned function is required and what values are being passed into it as parameters (r,a) and the role of Object.create(null). It would be great if could explain your code in steps. Thanks!
|
1

var data = [{item: 'abc',category: 'car',price: 1000},{item:'abc',category: 'bike',price: 1000},{item: 'abc',category: 'car',price: 2000},{item: 'abc',category: 'car',price: 3000},{item: 'abc',category: 'bike',price: 500},{item: 'abc',category: 'plane',price: 9000}];

var groupArr = [];
var res = data.reduce(function(all, item, index){
    var i = groupArr.findIndex(x => x.category == item.category);
    if(i <= -1)
        groupArr.push({category: item.category, price: item.price});
    else
        groupArr[i].price += item.price;
        
    return all;
},{})
console.log(groupArr)

Comments

0

See this if this helps

var obj = [{
  item: 'abc',
  category: 'car',
  price: 1000
}, {
  item: 'abc',
  category: 'bike',
  price: 1000
}, {
  item: 'abc',
  category: 'car',
  price: 2000
}, {
  item: 'abc',
  category: 'car',
  price: 3000
}, {
  item: 'abc',
  category: 'bike',
  price: 500
}, {
  item: 'abc',
  category: 'plane',
  price: 9000
}]

var newobj = {};

obj.forEach(function(record) {
  newobj[record.category] = {
    category: record.category,
    price : newobj[record.category] && newobj[record.category].price ? newobj[record.category].price + record.price : record.price
  }
})
var result = [];
Object.keys(newobj).forEach(function(key) {
    result.push(newobj[key])
})
console.log(result);

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.