Sharif
Sharif

Reputation: 373

Array of objects to array of objects grouped by propery

I'm struggling to come up with an efficient way of grouping array of objects by a property into another array.

I checked some solutions from related questions and answers however struggling to achieve needed data structure.

Sample data incoming data (taken from this q)

const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}, ];

here is what I'm trying to achieve:

const carsTransformed = [{
  'audi': [{
    'model': 'r8',
    'year': '2012'
  }, {
    'model': 'rs5',
    'year': '2013'
  },],
},
{
  'ford': [{
    'model': 'mustang',
    'year': '2012'
  }, {
    'model': 'fusion',
    'year': '2015'
  }],
},
{
  'kia': [{
    'model': 'optima',
    'year': '2012'
  }]
}
];

so far tried to use

const arrayGroupBy = (array, property) => {
  return array.reduce((map, object) => {
    (map[object[property]] = map[object[property]] || []).push(object);
    return map;
  }, {});
};

with some attempts to manipulate resulting object via Object.entries(obj).map() getting some weird results :(

I'll appreciate any ideas on how to achieve this.

Edit: Trying to not use any third party libraries.

Upvotes: 2

Views: 73

Answers (6)

Guvaliour
Guvaliour

Reputation: 417

 let cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}, ];

let rqObj = cars.reduce((acc,ele)=>{
  let nObj = JSON.parse(JSON.stringify(ele));
  delete nObj['make'];
  let obj = {[ele.make]:[nObj]};
  let pos = acc.findIndex(el=>Object.keys(el)[0]==ele.make);
  (pos == -1) ? acc.push(obj) : acc[pos][ele.make] = [...acc[pos][ele.make],...obj[ele.make]];
  return acc;
},[])
console.log(rqObj)

Upvotes: 1

epascarello
epascarello

Reputation: 207531

This is not really compact, but using delete you can remove the property from the object.

const groupBy = (obj, property = 'id') =>
  Object.entries(obj.reduce((map, item) => {
    var key = item[property]
    map[key] = map[key] || []
    const cleaned = Object.assign({}, item)
    delete cleaned[property]
    map[key].push(cleaned)
    return map
  }, {}))
  .map(([k, a]) => ({
    [k]: a
  }))

const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}, ];

console.log(groupBy(cars, 'make'))

Upvotes: 2

mscdeveloper
mscdeveloper

Reputation: 2889

Maybe Like This:

const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}, ];

function carsTransformed(_cars){
	var out = {};
	for(var key in _cars){
		var car_brand = _cars[key].make;
		if(!out[car_brand]){
			out[car_brand] = [];
		}
		delete(_cars[key].make);
		out[car_brand].push(_cars[key]);
	}
	return out;
}
console.log(carsTransformed(cars));

Upvotes: 0

hendrixchord
hendrixchord

Reputation: 5434

let group = {};
cars.forEach((c) => {
    if (!group[c.make]) group[c.make] = [];
    group[c.make] = [...group[c.make], cars.filter(m => m.make === c.make)];
});
console.log(group);

or you can achieve this via lodash library

_.mapValues(_.groupBy(cars, 'make'));

Upvotes: 0

Adam Orłowski
Adam Orłowski

Reputation: 4464

If you want Object here is the solution:

const carsTransformedObject = cars.reduce((acc, next) => {
  if (acc[next.make]) {
    acc[next.make].push({model: next.model, year: next.year})
  } else {
    acc[next.make] = [{model: next.model, year: next.year}]
  }
  return acc
}, {})

If you want this object to be in Array like you showed just do: const carsTransformed = [carsTransformedObject]

That's it 😉

You can try it here

Upvotes: 0

Al Hill
Al Hill

Reputation: 479

const carsTransformed = [...new Set(cars.map(c => c.make))].map(make => {
    return {
        [make]: cars.filter(cs => cs.make === make)
    }
})

Upvotes: 1

Related Questions