Reputation: 13771
Does anyone know of a way (lodash if possible too) to group an array of objects by an object key then create a new array of objects based on the grouping? For example, I have an array of car objects:
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'
},
];
I want to make a new array of car objects that's grouped by make
:
const cars = {
'audi': [
{
'model': 'r8',
'year': '2012'
}, {
'model': 'rs5',
'year': '2013'
},
],
'ford': [
{
'model': 'mustang',
'year': '2012'
}, {
'model': 'fusion',
'year': '2015'
}
],
'kia': [
{
'model': 'optima',
'year': '2012'
}
]
}
Upvotes: 426
Views: 690545
Reputation: 5519
Object.groupBy()
and Map.groupBy()
has reached Stage 4!It has already been implemented on most major browsers (see caniuse) and so you are able to do 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' }
];
const grouped = Object.groupBy(cars, item => item.make);
console.log(grouped);
which will output:
{
audi: [
{ make: 'audi', model: 'r8', year: '2012' },
{ make: 'audi', model: 'rs5', year: '2013' }
],
ford: [
{ make: 'ford', model: 'mustang', year: '2012' },
{ make: 'ford', model: 'fusion', year: '2015' }
],
kia: [
{ make: 'kia', model: 'optima', year: '2012' }
]
}
You can also use this core-js polyfill:
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' }
];
const grouped = Object.groupBy(cars, item => item.make);
//console.log(grouped);
// Optional: remove the "make" property from resulting object
const entriesUpdated = Object
.entries(grouped)
.map(([key, value]) => [
key,
value.map(({make, ...rest}) => rest)
]);
const noMake = Object.fromEntries(entriesUpdated);
console.log(noMake);
<script src="https://unpkg.com/[email protected]/minified.js"></script>
Upvotes: 8
Reputation: 3489
While it isn't in the interest of answering your question with the most optimal or efficient code, I thought this a fun oportunity to turn this into a Code Golf exercise.
This is a slightly altered version of @metakungfus answer, main difference being that it omits the original key from the resulting objects since it's no longer needed on the object itself in some cases since it's now available in the parent object.
Additionally, it heavily utilizes object, parameter and array expansion along with the reduce JavaScript function
.
const groupBy=(_k, a)=>a.reduce((r,{[_k]:k,...p})=>({...r,...{[k]: (r[k]?[...r[k],{...p}]:[{...p}])}}),{});
Considering your original input object:
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('make', cars));
Would result in:
{
audi: [
{ model: 'r8', year: '2012' },
{ model: 'rs5', year: '2013' }
],
ford: [
{ model: 'mustang', year: '2012' },
{ model: 'fusion', year: '2015' }
],
kia: [
{ model: 'optima', year: '2012' }
]
}
Upvotes: -1
Reputation: 7679
There is absolutely no reason to download a 3rd party library to achieve this simple problem, like the above solutions suggest.
The one line version to group a list
of objects by a certain key
in es6:
const groupByKey = (list, key) => list.reduce((hash, obj) => ({...hash, [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}), {})
The longer version that filters out the objects without the key
:
function groupByKey(array, key) {
return array
.reduce((hash, obj) => {
if(obj[key] === undefined) return hash;
return Object.assign(hash, { [obj[key]]:( hash[obj[key]] || [] ).concat(obj)})
}, {})
}
var 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(groupByKey(cars, 'make'))
NOTE: It appear the original question asks how to group cars by make, but omit the make in each group. So the short answer, without 3rd party libraries, would look like this:
var groupByKey = (list, key) => list.reduce((map, obj) => {
const group = obj[key];
if(map.has(key)) {
map.get(group).push(obj);
} else {
map.set(group, [obj])
}
return map
}, new Map())
var 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(...groupByKey(cars, 'make', {omitKey:true}))
Upvotes: 142
Reputation: 386786
In plain Javascript, you could use Array#reduce
with an object
var 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' }],
result = cars.reduce(function (r, a) {
r[a.make] = r[a.make] || [];
r[a.make].push(a);
return r;
}, Object.create(null));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
UPDATE 2023
Now with Object.groupBy
. It takes an iterable and a function for grouping.
var 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' }],
result = Object.groupBy(cars, ({ make }) => make);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 576
Reputation: 135406
2023
Object.groupBy has arrived in native JavaScript -
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(Object.groupBy(
cars,
car => car.make,
))
In supported browsers, as of October 2023 -
{
"audi": [
{
"make": "audi",
"model": "r8",
"year": "2012"
},
{
"make": "audi",
"model": "rs5",
"year": "2013"
}
],
"ford": [
{
"make": "ford",
"model": "mustang",
"year": "2012"
},
{
"make": "ford",
"model": "fusion",
"year": "2015"
}
],
"kia": [
{
"make": "kia",
"model": "optima",
"year": "2012"
}
]
}
Browser | Version | |
---|---|---|
Chrome | 117 | ✅ |
Edge | 117 | ✅ |
Firefox | 119 | ✅ |
Opera | 103 | ✅ |
Safari | TP | ⚠️ |
Upvotes: 1
Reputation: 92677
Try
groupBy= (a,f) => a.reduce( (x,c) => (x[f(c)]??=[]).push(c)&&x, {} )
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'
},
];
const groupBy= (a,f) => a.reduce( (x,c) => (x[f(c)]??=[]).push(c)&&x, {} )
console.log('gr', groupBy(cars, o=>o.make));
This answer is inspired by cdiggins answer and Endless comment (without key remove in final objects). The improvement is that we have same small size but function interface groupBy(a,f)
not contains additional redundant variables. To get array of grouped arrays you can use Object.values(groupBy(cars, o=>o.make))
Upvotes: -1
Reputation: 237
This is a generic function which will return Array groupBy its own key.
const getSectionListGroupedByKey = < T > (
property: keyof T,
List: Array < T >
): Array < {
title: T[keyof T];data: Array < T >
} > => {
const sectionList: Array < {
title: T[keyof T];data: Array < T >
} > = [];
if (!property || !List ? .[0] ? .[property]) {
return [];
}
const groupedTxnListMap: Map < T[keyof T], Array < T >> = List.reduce((acc, cv) => {
const keyValue: T[keyof T] = cv[property];
if (acc.has(keyValue)) {
acc.get(keyValue) ? .push(cv);
} else {
acc.set(keyValue, [cv]);
}
return acc;
}, new Map < T[keyof T], Array < T >> ());
groupedTxnListMap.forEach((value, key) => {
sectionList.push({
title: key,
data: value
});
});
return sectionList;
};
// Example
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'
}, ];
const result = getSectionListGroupedByKey('make', cars);
console.log('result: ', result)
Upvotes: -1
Reputation: 379
letfinaldata=[]
let data =[{id:1,name:"meet"},{id:2,name:"raj"},{id:1,name:"hari"},{id:3,name:"hari"},{id:2,name:"ram"}]
data = data.map((item)=>
{
return {...item,
name: [item.name]
}
}) // Converting the name key from string to array
let temp = [];
for(let i =0 ;i<data.length;i++)
{
const index = temp.indexOf(data[i].id) // Checking if the object id is already present
if(index>=0)
{
letfinaldata[index].name = [...letfinaldata[index].name,...data[i].name] // If present then append the name to the name of that object
}
else{
temp.push(data[i].id); // Push the checked object id
letfinaldata.push({...data[i]}) // Push the object
}
}
console.log(letfinaldata)
Output
[ { id: 1, name: [ 'meet', 'hari' ] },
{ id: 2, name: [ 'raj', 'ram' ] },
{ id: 3, name: [ 'hari' ] } ]
Upvotes: 1
Reputation: 2048
const groupBy = (array, callback) => {
const groups = {};
array.forEach((element) => {
const groupName = callback(element);
if (groupName in groups) {
groups[groupName].push(element);
} else {
groups[groupName] = [element];
}
});
return groups;
};
or for fancy pants:
(() => {
Array.prototype.groupBy = function (callback) {
const groups = {};
this.forEach((element, ...args) => {
const groupName = callback(element, ...args);
if (groupName in groups) {
groups[groupName].push(element);
} else {
groups[groupName] = [element];
}
});
return groups;
};
})();
const res = [{ name: 1 }, { name: 1 }, { name: 0 }].groupBy(({ name }) => name);
// const res = {
// 0: [{name: 0}],
// 1: [{name: 1}, {name: 1}]
// }
This is a polyfill for the MDN Array.groupBy function.
Upvotes: -1
Reputation: 42520
You are looking for _.groupBy()
.
Removing the property you are grouping by from the objects should be trivial if required:
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'
}];
const grouped = _.groupBy(cars, car => car.make);
console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>
Upvotes: 156
Reputation: 4780
Another one solution:
var 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'},
];
const reducedCars = cars.reduce((acc, { make, model, year }) => (
{
...acc,
[make]: acc[make] ? [ ...acc[make], { model, year }] : [ { model, year } ],
}
), {});
console.log(reducedCars);
Upvotes: 5
Reputation: 1696
Just simple forEach loop will work here without any library
var 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 ObjMap ={};
cars.forEach(element => {
var makeKey = element.make;
if(!ObjMap[makeKey]) {
ObjMap[makeKey] = [];
}
ObjMap[makeKey].push({
model: element.model,
year: element.year
});
});
console.log(ObjMap);
Upvotes: 9
Reputation: 22483
Timo's answer is how I would do it. Simple _.groupBy
, and allow some duplications in the objects in the grouped structure.
However the OP also asked for the duplicate make
keys to be removed. If you wanted to go all the way:
var grouped = _.mapValues(_.groupBy(cars, 'make'),
clist => clist.map(car => _.omit(car, 'make')));
console.log(grouped);
Yields:
{ audi:
[ { model: 'r8', year: '2012' },
{ model: 'rs5', year: '2013' } ],
ford:
[ { model: 'mustang', year: '2012' },
{ model: 'fusion', year: '2015' } ],
kia:
[ { model: 'optima', year: '2012' } ]
}
If you wanted to do this using Underscore.js, note that its version of _.mapValues
is called _.mapObject
.
Upvotes: 200
Reputation: 994
Agree that unless you use these often there is no need for an external library. Although similar solutions are available, I see that some of them are tricky to follow here is a gist that has a solution with comments if you're trying to understand what is happening.
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'
}, ];
/**
* Groups an array of objects by a key an returns an object or array grouped by provided key.
* @param array - array to group objects by key.
* @param key - key to group array objects by.
* @param removeKey - remove the key and it's value from the resulting object.
* @param outputType - type of structure the output should be contained in.
*/
const groupBy = (
inputArray,
key,
removeKey = false,
outputType = {},
) => {
return inputArray.reduce(
(previous, current) => {
// Get the current value that matches the input key and remove the key value for it.
const {
[key]: keyValue
} = current;
// remove the key if option is set
removeKey && keyValue && delete current[key];
// If there is already an array for the user provided key use it else default to an empty array.
const {
[keyValue]: reducedValue = []
} = previous;
// Create a new object and return that merges the previous with the current object
return Object.assign(previous, {
[keyValue]: reducedValue.concat(current)
});
},
// Replace the object here to an array to change output object to an array
outputType,
);
};
console.log(groupBy(cars, 'make', true))
Upvotes: 4
Reputation: 835
I made a benchmark to test the performance of each solution that don't use external libraries.
The reduce()
option, posted by @Nina Scholz seems to be the optimal one.
Upvotes: 1
Reputation: 438
I love to write it with no dependency/complexity just pure simple js.
const mp = {}
const cars = [
{
model: 'Imaginary space craft SpaceX model',
year: '2025'
},
{
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'
}
]
cars.forEach(c => {
if (!c.make) return // exit (maybe add them to a "no_make" category)
if (!mp[c.make]) mp[c.make] = [{ model: c.model, year: c.year }]
else mp[c.make].push({ model: c.model, year: c.year })
})
console.log(mp)
Upvotes: 1
Reputation: 3721
var 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'
}].reduce((r, car) => {
const {
model,
year,
make
} = car;
r[make] = [...r[make] || [], {
model,
year
}];
return r;
}, {});
console.log(cars);
Upvotes: 23
Reputation: 6314
I'd leave REAL GROUP BY
for JS Arrays example exactly the same this task here
const inputArray = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];
var outObject = inputArray.reduce(function(a, e) {
// GROUP BY estimated key (estKey), well, may be a just plain key
// a -- Accumulator result object
// e -- sequentally checked Element, the Element that is tested just at this itaration
// new grouping name may be calculated, but must be based on real value of real field
let estKey = (e['Phase']);
(a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e);
return a;
}, {});
console.log(outObject);
Upvotes: 12
Reputation: 49
Grouped Array of Object in typescript with this:
groupBy (list: any[], key: string): Map<string, Array<any>> {
let map = new Map();
list.map(val=> {
if(!map.has(val[key])){
map.set(val[key],list.filter(data => data[key] == val[key]));
}
});
return map;
});
Upvotes: 1
Reputation: 349
Here is a solution inspired from Collectors.groupingBy() in Java:
function groupingBy(list, keyMapper) {
return list.reduce((accummalatorMap, currentValue) => {
const key = keyMapper(currentValue);
if(!accummalatorMap.has(key)) {
accummalatorMap.set(key, [currentValue]);
} else {
accummalatorMap.set(key, accummalatorMap.get(key).push(currentValue));
}
return accummalatorMap;
}, new Map());
}
This will give a Map object.
// Usage
const carMakers = groupingBy(cars, car => car.make);
Upvotes: 0
Reputation: 13723
Here is another solution to it. As requested.
I want to make a new array of car objects that's grouped by make:
function groupBy() {
const key = 'make';
return cars.reduce((acc, x) => ({
...acc,
[x[key]]: (!acc[x[key]]) ? [{
model: x.model,
year: x.year
}] : [...acc[x[key]], {
model: x.model,
year: x.year
}]
}), {})
}
Output:
console.log('Grouped by make key:',groupBy())
Upvotes: -2
Reputation: 91
Prototype version using ES6 as well. Basically this uses the reduce function to pass in an accumulator and current item, which then uses this to build your "grouped" arrays based on the passed in key. the inner part of the reduce may look complicated but essentially it is testing to see if the key of the passed in object exists and if it doesn't then create an empty array and append the current item to that newly created array otherwise using the spread operator pass in all the objects of the current key array and append current item. Hope this helps someone!.
Array.prototype.groupBy = function(k) {
return this.reduce((acc, item) => ((acc[item[k]] = [...(acc[item[k]] || []), item]), acc),{});
};
const projs = [
{
project: "A",
timeTake: 2,
desc: "this is a description"
},
{
project: "B",
timeTake: 4,
desc: "this is a description"
},
{
project: "A",
timeTake: 12,
desc: "this is a description"
},
{
project: "B",
timeTake: 45,
desc: "this is a description"
}
];
console.log(projs.groupBy("project"));
Upvotes: 3
Reputation: 21
const reGroup = (list, key) => {
const newGroup = {};
list.forEach(item => {
const newItem = Object.assign({}, item);
delete newItem[key];
newGroup[item[key]] = newGroup[item[key]] || [];
newGroup[item[key]].push(newItem);
});
return newGroup;
};
const animals = [
{
type: 'dog',
breed: 'puddle'
},
{
type: 'dog',
breed: 'labradoodle'
},
{
type: 'cat',
breed: 'siamese'
},
{
type: 'dog',
breed: 'french bulldog'
},
{
type: 'cat',
breed: 'mud'
}
];
console.log(reGroup(animals, 'type'));
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(reGroup(cars, 'make'));
Upvotes: 1
Reputation: 560
Building on the answer by @Jonas_Wilms if you do not want to type in all your fields:
var result = {};
for ( let { first_field, ...fields } of your_data )
{
result[first_field] = result[first_field] || [];
result[first_field].push({ ...fields });
}
I didn't make any benchmark but I believe using a for loop would be more efficient than anything suggested in this answer as well.
Upvotes: 0
Reputation: 192857
With lodash/fp you can create a function with _.flow()
that 1st groups by a key, and then map each group, and omits a key from each item:
const { flow, groupBy, mapValues, map, omit } = _;
const groupAndOmitBy = key => flow(
groupBy(key),
mapValues(map(omit(key)))
);
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' }];
const groupAndOmitMake = groupAndOmitBy('make');
const result = groupAndOmitMake(cars);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
Upvotes: 0
Reputation: 7890
For cases where key can be null and we want to group them as others
var 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'},
{'make':'kia','model':'optima','year':'2033'},
{'make':null,'model':'zen','year':'2012'},
{'make':null,'model':'blue','year':'2017'},
];
result = cars.reduce(function (r, a) {
key = a.make || 'others';
r[key] = r[key] || [];
r[key].push(a);
return r;
}, Object.create(null));
Upvotes: 5
Reputation: 1742
I liked @metakunfu answer, but it doesn't provide the expected output exactly. Here's an updated that get rid of "make" in the final JSON payload.
var 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'
},
];
result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {})
console.log(JSON.stringify(result));
Output:
{
"audi":[
{
"model":"r8",
"year":"2012"
},
{
"model":"rs5",
"year":"2013"
}
],
"ford":[
{
"model":"mustang",
"year":"2012"
},
{
"model":"fusion",
"year":"2015"
}
],
"kia":[
{
"model":"optima",
"year":"2012"
}
]
}
Upvotes: 2
Reputation: 217
function groupBy(data, property) {
return data.reduce((acc, obj) => {
const key = obj[property];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});
}
groupBy(people, 'age');
Upvotes: 4
Reputation: 138497
Its also possible with a simple for
loop:
const result = {};
for(const {make, model, year} of cars) {
if(!result[make]) result[make] = [];
result[make].push({ model, year });
}
Upvotes: 23
Reputation: 10624
You can also make use of array#forEach()
method 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' }];
let newcars = {}
cars.forEach(car => {
newcars[car.make] ? // check if that array exists or not in newcars object
newcars[car.make].push({model: car.model, year: car.year}) // just push
: (newcars[car.make] = [], newcars[car.make].push({model: car.model, year: car.year})) // create a new array and push
})
console.log(newcars);
Upvotes: 4