I faced a challenge where I needed to summarize an array of objects by the object's keys. I found a solution, but I can't shake off the feeling, that my approach is pretty naive:
const objArr = [
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 2, val: "๐ฅฆ" },
{ id: 2, val: "๐ฝ" },
{ id: 2, val: "๐ถ" },
];
let tempArr = [];
let uniqueIdArr = [];
let sortedArr = [];
objArr.forEach((obj) => {
tempArr.push(obj.id);
uniqueIdArr = [...new Set(tempArr)];
});
uniqueIdArr.forEach((uniqueId) => {
let arr = [];
objArr.forEach((obj) => {
if (obj.id == uniqueId) {
arr.push(obj.val);
}
});
sortedArr.push({
id: uniqueId,
vals: arr,
});
});
console.log(sortedArr);
// Output: [{ id: 1, vals: [ '๐', '๐', '๐' ] }, { id: 2, vals: [ '๐ฅฆ', '๐ฝ', '๐ถ' ] }]
Maybe there is something I don't know about JavaScript's array methods yet? Is this approach totally wrong? Is there another way, so that I could reduce the code and make it more elegant?
So many questions...
Any hint or explanation would be much appreciated. ๐
Thanks in advance
J.
Side note: normally, you're supposed to have unique id, other than that, you're doing multiple passes over the source array, which means in worse-case scenario (when there are no duplicate id's) you get O(nยฒ) time complexity, hence execution time will spike rapidly as the number of items in array grows.
If you seek to optimize the performance, you may probably employ Map together with Array.prototype.reduce(), like this:
const objArr = [
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 2, val: "๐ฅฆ" },
{ id: 2, val: "๐ฝ" },
{ id: 2, val: "๐ถ" },
]
const grouppedArr = [
...objArr
.reduce((acc, {id, val}) => {
const group = acc.get(id)
group
? group.vals.push(val)
: acc.set(id, {id, vals:[val]})
return acc
}, new Map)
.values()
]
console.log(grouppedArr)
you can use Array.prototype.reduce to make your code bit shorter:
const objArr = [
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 2, val: "๐ฅฆ" },
{ id: 2, val: "๐ฝ" },
{ id: 2, val: "๐ถ" },
];
let result = objArr.reduce((acc,e) => {
let idx = acc.findIndex(s => s.id === e.id)
if(idx > -1){
acc[idx].vals.push(e.val)
}
else{
acc.push({id:e.id,vals:[e.val]})
}
return acc
},[])
console.log(result)
Your ideas are good and explicit, but far from being optimal.
const objArr = [
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 1, val: "๐" },
{ id: 2, val: "๐ฅฆ" },
{ id: 2, val: "๐ฝ" },
{ id: 2, val: "๐ถ" },
];
const idValMap = new Map();
objArr.forEach(o=>{
let vals = idValMap.get(o.id);
if(!vals){
vals = [];
idValMap.set(o.id,vals);
}
vals.push(o.val);
});
console.log(Array.from(idValMap.entries()));
You can do most of it in just one loop. Take the key, check if you saw it already, if not initialize. That's it