'merge sub-array if other key-value pair match
I have this simple array, which I want to merge key b value if key a in the whole array matches.
const array = [
{a: 1, b: ['Foo']},
{a: 1, b: ['Bar']},
{a: 1, b: ['Baz']},
{a: 2, b: ['Foo']},
{a: 3, b: ['Foo']},
]
into
const array = [
{a: 1, b: ['Foo','Bar','Baz']},
{a: 2, b: ['Foo']},
{a: 3, b: ['Foo']},
]
is there a way to do it? Any help would be appreciated!
EDIT 1 : What I am lacking here is the logic to grab all similar key a
Solution 1:[1]
Use reduce to iterate over the array of objects to create an object where the keys match the object's a value, adding the various b values to their arrays. Then use Object.values to create a new array.
const arr = [
{a: 1, b: ['Foo']},
{a: 1, b: ['Bar']},
{a: 1, b: ['Baz']},
{a: 2, b: ['Foo']},
{a: 3, b: ['Foo']},
];
// For each iteration pass in the accumulator
// and the current object
const out = arr.reduce((acc, c) => {
// Assign the value of `a` to `key`
const key = c.a;
// If the key doesn't exist on the accumulator
// set a new object
acc[key] = acc[key] || { a: key, b: [] };
// And then push the first element of `b`
// into the object's array
acc[key].b.push(c.b[0]);
// Return the accumulator
return acc;
}, {});
// And then finally get the Object.values from
// your returned object to make an array
console.log(Object.values(out));
Solution 2:[2]
You can use reduce for that
First you create an object with the value of a as key and you group all b with the same a
Then you simply remove the object keys using Object.values
const array = [
{a: 1, b: ['Foo']},
{a: 1, b: ['Bar']},
{a: 1, b: ['Baz']},
{a: 2, b: ['Foo']},
{a: 3, b: ['Foo']},
]
const grouped = Object.values(array.reduce(
(res, {a, b}) => {
const existing = res[a] || {b: []}
return {
...res,
[a]: {a, b: [...existing.b, ...b]}
}
}, {}
))
console.log(grouped)
Solution 3:[3]
Using reduce you can merge the values of b and create a new array.
const array = [
{a: 1, b: ['Foo']},
{a: 1, b: ['Bar']},
{a: 1, b: ['Baz']},
{a: 2, b: ['Foo']},
{a: 3, b: ['Foo']},
];
const result = array.reduce((t, { a, b }, _, arr) => {
if (t.some(v => v.a === a))
return t; // if a is already handled, skip
return [
...t,
{
a,
b: arr
.filter(v => v.a === a) // filter out items with different "a" value
.reduce((t, v) => [...t, ...v.b], []), // merge the values of "b"
}
];
}, []);
console.log(result);
Solution 4:[4]
Here I use Set to make sure no duplicate values. And finally map it back to an array.
const array = [
{a: 1, b: ['Foo']},
{a: 1, b: ['Bar']},
{a: 1, b: ['Baz']},
{a: 2, b: ['Foo']},
{a: 3, b: ['Foo']},
]
const o = array.reduce((acc, obj) => {
if(!(acc[obj?.a] instanceof Set))
acc[obj.a] = new Set()
acc[obj.a].add(obj.b)
return acc
}, {})
const result = Object.entries(o).map(([a, b]) => ({a, b: Array.from(b) }))
console.log(result)
Solution 5:[5]
Using JavaScript Map
const array = [
{ a: 1, b: ["Foo"] },
{ a: 1, b: ["Bar"] },
{ a: 1, b: ["Baz"] },
{ a: 2, b: ["Foo"] },
{ a: 3, b: ["Foo"] },
];
const grouped = Array.from(
array
.reduce((m, o) => {
if (m.has(o.a)) {
const currObj = m.get(o.a);
m.set(o.a, { ...currObj, b: [...currObj.b, ...o.b] });
} else {
m.set(o.a, o);
}
return m;
}, new Map())
.values()
);
console.log(grouped);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Andy |
| Solution 2 | R4ncid |
| Solution 3 | |
| Solution 4 | |
| Solution 5 | Anonymous Panda |
