Reputation: 14198
I have the object arrays like below:
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
And what I want is to group by id, name
and list out rating
belonging its like below
const users = [
{
id: 1,
name: "Spider-man",
rating: {top: 1, middle: 4.5, bottom: 5}
},
{
id: 2,
name: "Iron man",
rating: {top: 3, middle: 3.5, bottom: 2}
},
{
id: 3,
name: "Hulk",
rating: {top: 5, middle: 1.5, bottom: 4}
}
];
I've tried this approach but seems it can achieve in more ways such as .reduce
, for...of
with more elegant, right?
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
var result = obj.top.map(x => ({
id: x.id,
name: x.name,
rating: {
top: obj.top.find(t => t.id === x.id).rating,
middle: obj.middle.find(t => t.id === x.id).rating,
bottom: obj.bottom.find(t => t.id === x.id).rating,
}
}));
console.log(result);
Any other ways to achieve it? Thanks in advance.
Upvotes: 3
Views: 186
Reputation: 14198
Using Dictionary
along with Logical nullish assignment (??=)
The main idea includes 2 steps:
[key, values]
of the object.values
of the object, we loop to determine which the user's rating
along with key
belongs by user.id
.const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
// Refactor code: using Dictionary along with `Logical nullish assignment (??=)` .
var result = Object.entries(obj).reduce((acc, [key, values]) => {
values.forEach(v => {
acc[v.id] ??= {...v, rating: {}};
acc[v.id].rating[key] = v.rating;
});
return acc;
}, {});
console.log(Object.values(result));
/* Old versions: using Array approach
var result = Object.entries(obj).reduce((acc, [key, values]) => {
values.forEach(v => {
var x = acc.find(r => r.id === v.id);
if(x !== undefined){
x.rating[key] = v.rating;
}else{
x = { id: v.id, name: v.name, rating: {[key]: v.rating} };
acc.push(x);
}
});
return acc;
}, []);
*/
More detailed explanation:
dictionary
approach instead of array
, you can achieve it with the highest performance both time
and space
complexity (as @CertainPerformance's comment).MDN Web docs
said that:The logical nullish assignment (x ??= y) operator
only assigns if x is nullish (null or undefined)
.
Upvotes: 1
Reputation: 14871
You could do it in a one-liner way
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 },
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 },
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 },
],
};
const res = Array.from(
Object.entries(obj)
.flatMap(([rater, ratee]) =>
ratee.map(({ id, name, rating }) => ({
id,
name,
[rater]: rating,
}))
)
.reduce(
(acc, { id, ...restInfo }) =>
acc.set(id, { ...(acc.get(id) || {}), ...restInfo }),
new Map()
)
).map(([id, { name, ...rating }]) => ({ id, name, rating }));
console.log(res);
Upvotes: 1
Reputation: 370619
You need to map one of the subarrays to find each character's rating, so I think your current approach is pretty reasonable. You can make it a bit less repetitive by making an array of the properties (top
, middle
, bot
) beforehand, then iterating over them instead of listing each different one:
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
const props = ['top', 'middle', 'bottom'];
var result = obj.top.map(x => ({
id: x.id,
name: x.name,
rating: Object.fromEntries(
props.map(prop =>
[prop, obj[prop].find(t => t.id === x.id).rating]
)
)
}));
console.log(result);
Another approach that's less computationally complex:
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
const byName = {};
for (const [prop, arr] of Object.entries(obj)) {
for (const item of arr) {
byName[item.name] ??= { ...item, rating: {} };
byName[item.name].rating[prop] = item.rating;
}
}
console.log(Object.values(byName));
Upvotes: 3
Reputation:
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
var result = [];
for(let [key, values] of Object.entries(obj))
for(let item of values){
let x = result.find(r => r.id === item.id);
if(x !== undefined){
x.rating[key] = item.rating;
}else{
x = { id: item.id, name: item.name, rating: {[key]: item.rating} };
result.push(x);
}
}
console.log(result);
Upvotes: 0
Reputation:
const obj = {
top: [
{ id: 1, name: "Spider-man", rating: 1 },
{ id: 2, name: "Iron man", rating: 3 },
{ id: 3, name: "Hulk", rating: 5 }
],
middle: [
{ id: 1, name: "Spider-man", rating: 4.5 },
{ id: 2, name: "Iron man", rating: 3.5 },
{ id: 3, name: "Hulk", rating: 1.5 }
],
bottom: [
{ id: 1, name: "Spider-man", rating: 5 },
{ id: 2, name: "Iron man", rating: 2 },
{ id: 3, name: "Hulk", rating: 4 }
]
};
const tempArr1 = Object.keys(obj).reduce((arr, key) => {
obj[key].forEach((item) => {
arr = [...arr, { ...item, rating: { [key]: item.rating } }];
});
return arr;
}, []);
const result = tempArr1.reduce((arr, el) => {
let tempObj = { ...el };
const index = arr.findIndex((tempItem) => tempItem.id === tempObj.id);
if (~index) {
arr[index] = {
...tempObj,
rating: {
...arr[index].rating,
...tempObj.rating
}
};
} else {
arr = [...arr, tempObj];
}
return arr;
}, []);
console.log(result);
Upvotes: 0