Given an array like this:
[
{ id: 1, emailAddresses: ["[email protected]", "[email protected]"] },
{ id: 2, emailAddresses: ["[email protected]" },
{ id: 3, emailAddresses: ["[email protected]", "[email protected]", "[email protected]"]
]
How could I use Javascript to reduce this to an array like this:
[
{ id: 1, emailAddress: "[email protected]" },
{ id: 1, emailAddress: "[email protected]" },
{ id: 2, emailAddress: "[email protected]" },
{ id: 3, emailAddress: "[email protected]" },
{ id: 3, emailAddress: "[email protected]" },
{ id: 3, emailAddress: "[email protected]" }
]
I've read about the functions reduce
, flat
, map
and so on and read lots of the questions on SO about using them but I can't find anything that's asking quite the same as this and I can't get my head around using those functions to do it.
Given an array like this:
[
{ id: 1, emailAddresses: ["[email protected]", "[email protected]"] },
{ id: 2, emailAddresses: ["[email protected]" },
{ id: 3, emailAddresses: ["[email protected]", "[email protected]", "[email protected]"]
]
How could I use Javascript to reduce this to an array like this:
[
{ id: 1, emailAddress: "[email protected]" },
{ id: 1, emailAddress: "[email protected]" },
{ id: 2, emailAddress: "[email protected]" },
{ id: 3, emailAddress: "[email protected]" },
{ id: 3, emailAddress: "[email protected]" },
{ id: 3, emailAddress: "[email protected]" }
]
I've read about the functions reduce
, flat
, map
and so on and read lots of the questions on SO about using them but I can't find anything that's asking quite the same as this and I can't get my head around using those functions to do it.
5 Answers
Reset to default 3You could use flatMap
const input = [
{ id: 1, emailAddresses: ["[email protected]", "[email protected]"] },
{ id: 2, emailAddresses: ["[email protected]"] },
{ id: 3, emailAddresses: ["[email protected]", "[email protected]", "[email protected]"] }
]
const output = input.flatMap(o =>
o.emailAddresses.map(e => ({ id: o.id, emailAddress: e }) )
)
console.log(output)
If flatMap
is not supported, you could use a nested for...of
loop:
const input = [{id:1,emailAddresses:["[email protected]","[email protected]"]},{id:2,emailAddresses:["[email protected]"]},{id:3,emailAddresses:["[email protected]","[email protected]","[email protected]"]}];
const output = []
for (const { id, emailAddresses } of input)
for (const emailAddress of emailAddresses)
output.push({ id, emailAddress })
console.log(output)
You can map over your data and then use reduce to flatten the resulting array:
const result = data
.map(datum => {
return datum.emailAddresses.map(emailAddress => {
return { id: datum.id, emailAddress };
});
})
.reduce((result, current) => {
return [...result, ...current];
}, []);
We can use Array.prototype.reduce
to go over each object in the array and take into consideration the multiple values in the emailAddress
property array and create separate object for each one and finally accumulate the new objects in the new array (r
):
const data = [
{ id: 1, emailAddresses: ["[email protected]", "[email protected]"] },
{ id: 2, emailAddresses: ["[email protected]"] },
{ id: 3, emailAddresses: ["[email protected]", "[email protected]", "[email protected]"]}
]
const flat = data.reduce((r, e) => {
e.emailAddresses.forEach((obj) => r.push({id: e.id, emailAddresses : obj }));
return r;
}, []);
console.log(flat);
You can use reduce and map
const data = [
{ id: 1, emailAddresses: ["[email protected]", "[email protected]"] },
{ id: 2, emailAddresses: ["[email protected]"] },
{ id: 3, emailAddresses: ["[email protected]", "[email protected]", "[email protected]"]}
]
const flat = (toFlatten) =>
toFlatten.reduce((r,c)=> {
r.push(...c.emailAddresses.map(email=>({id: c.id, emailAddress: email})))
return r
}, [])
console.log(flat(data))
Here is a solution that doesn't use any array prototype but does, instead, take advantage of function generators.
The script below iterates the array, acquire all keys of the element except emailAddresses
, which is handled separately, and for each email address it yields an object filled with the single email address and the rest of the data.
This solution iterate the original array only once.
Because it uses function generators, this solution is widely supported, it just won't work on IE due it's lack of support for function generators, despite babel or TSC can easily add patibility to that.
const input = [
{ id: 1, emailAddresses: ["[email protected]", "[email protected]"] },
{ id: 2, emailAddresses: ["[email protected]"] },
{ id: 3, emailAddresses: ["[email protected]", "[email protected]", "[email protected]"] }
];
function* flattenEmailAddresses(arr) {
for (var {emailAddresses, ...keys} of arr) {
for (var emailAddress of emailAddresses) yield {...keys, emailAddress};
}
}
console.log([...flattenEmailAddresses(input)]);