Reputation: 481
I'm trying to wrap my head around some more advanced uses of .reduce()
, but still have a long way to go. The current issue has left me kind of clueless.
Let's say I have a an array of objects that looks like this:
const people = [
{
name: "Henry",
id: "1934",
wormsEaten: 4
},
{
name: "Melinda",
id: "9283",
wormsEaten: 0
},
{
name: "James",
id: "1029",
wormsEaten: 4
},
{
name: "Charles",
id: "7210",
wormsEaten: 3
},
{
name: "Sasha",
id: "4431",
wormsEaten: 3
},
]
I now want to create a new array of arrays for each matching wormsEaten
value, i.e. an array that looks like this:
[
[
{
name: "Henry",
id: "1934",
wormsEaten: 4
},
{
name: "James",
id: "1029",
wormsEaten: 4
}
],
[
{
name: "Melinda",
id: "9283",
wormsEaten: 0
}
],
[
{
name: "Charles",
id: "7210",
wormsEaten: 3
},
{
name: "Sasha",
id: "4431",
wormsEaten: 3
}
]
]
My pseudocode-thinking brain says something along the lines of:
people.reduce((accumulator, currentPerson) => {
if(accumulator.wormsEaten === currentPerson.wormsEaten) {
return [
…
]
}
})
But that's roughly how far I get. I feel like I have a long way to go with this type of thinking and would be thankful if someone could help me in the right direction.
Upvotes: 0
Views: 64
Reputation: 12919
Sorting will be more efficient after grouping since you will have a shorter array.
const people = [{ name: "Melinda", id: "9283", wormsEaten: 0 }, { name: "James", id: "1029", wormsEaten: 4 }, { name: "Charles", id: "7210", wormsEaten: 3 }, { name: "Sasha", id: "4431", wormsEaten: 3 }, { name: "Henry", id: "1934", wormsEaten: -9 }];
const result = {};
for (const person of people) {
(result[person.wormsEaten] ??= []).push({ ...person });
}
const sortedResult = Object.values(result).sort(([a], [b]) => a.wormsEaten - b.wormsEaten)
console.log(sortedResult)
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you must sort before grouping you can maintain your sorted array ordering through the reduce by accumulating into a Map, which maintains insertion order; into an Array, which necessarily enforces insertion order; or keep using an object, but force all your properties to be strings.
Map
const
people = [{ name: "Melinda", id: "9283", wormsEaten: 0 }, { name: "James", id: "1029", wormsEaten: 4 }, { name: "Charles", id: "7210", wormsEaten: 3 }, { name: "Sasha", id: "4431", wormsEaten: 3 }, { name: "Henry", id: "1934", wormsEaten: -9 }],
sorted = people.sort((a, b) => a.wormsEaten - b.wormsEaten),
result = sorted.reduce((map, person) => {
if (!map.has(person.wormsEaten)) {
map.set(person.wormsEaten, []);
}
map.get(person.wormsEaten).push({ ...person });
return map;
}, new Map)
console.log([...result.values()])
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array
const
people = [{ name: "Melinda", id: "9283", wormsEaten: 0 }, { name: "James", id: "1029", wormsEaten: 4 }, { name: "Charles", id: "7210", wormsEaten: 3 }, { name: "Sasha", id: "4431", wormsEaten: 3 }, { name: "Henry", id: "1934", wormsEaten: -9 }],
sorted = people.sort((a, b) => a.wormsEaten - b.wormsEaten),
result = sorted.reduce((arr, person) => {
let groupIndex = arr.findIndex(_arr => _arr[0].wormsEaten === person.wormsEaten);
if (groupIndex === -1) {
groupIndex = arr.push([]) - 1;
}
arr[groupIndex].push({ ...person });
return arr;
}, []);
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Alternative implementation since you have already sorted the array by the property you will be grouping by.
const
people = [{ name: "Melinda", id: "9283", wormsEaten: 0 }, { name: "James", id: "1029", wormsEaten: 4 }, { name: "Charles", id: "7210", wormsEaten: 3 }, { name: "Sasha", id: "4431", wormsEaten: 3 }, { name: "Henry", id: "1934", wormsEaten: -9 }],
sorted = people.sort((a, b) => a.wormsEaten - b.wormsEaten),
result = sorted.reduce((arr, person) => {
if (person.wormsEaten !== arr[arr.length - 1]?.[0].wormsEaten) {
arr.push([])
}
arr[arr.length - 1].push({ ...person })
return arr;
}, []);
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Object with string properties
const
people = [{ name: "Melinda", id: "9283", wormsEaten: 0 }, { name: "James", id: "1029", wormsEaten: 4 }, { name: "Charles", id: "7210", wormsEaten: 3 }, { name: "Sasha", id: "4431", wormsEaten: 3 }, { name: "Henry", id: "1934", wormsEaten: -9 }],
sorted = people.sort((a, b) => a.wormsEaten - b.wormsEaten),
result = sorted.reduce((obj, person) => {
(obj[`_${person.wormsEaten}`] || (obj[`_${person.wormsEaten}`] = [])).push({ ...person });
return obj;
}, {})
console.log(Object.values(result))
.as-console-wrapper { max-height: 100% !important; top: 0; }
A reduce()
call like this is equivalent to a standard for
loop with an internally passed accumulator. Here illustrated with a for..of
loop and using the nullish logical assignment operator (??=).
const
people = [{ name: "Melinda", id: "9283", wormsEaten: 0 }, { name: "James", id: "1029", wormsEaten: 4 }, { name: "Charles", id: "7210", wormsEaten: 3 }, { name: "Sasha", id: "4431", wormsEaten: 3 }, { name: "Henry", id: "1934", wormsEaten: -9 }],
sorted = people.sort((a, b) => a.wormsEaten - b.wormsEaten);
const result = {};
for (const person of people) {
(result[`_${person.wormsEaten}`] ??= []).push({ ...person });
}
console.log(Object.values(result))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0
Reputation: 20039
By grouping with wormsEaten
const people = [{name:"Henry",id:"1934",wormsEaten:4},{name:"Melinda",id:"9283",wormsEaten:0},{name:"James",id:"1029",wormsEaten:4},{name:"Charles",id:"7210",wormsEaten:3},{name:"Sasha",id:"4431",wormsEaten:3}]
const result = people.reduce((accumulator, currentPerson) => {
accumulator[currentPerson.wormsEaten] = accumulator[currentPerson.wormsEaten] || []
accumulator[currentPerson.wormsEaten].push(currentPerson)
return accumulator
}, {})
console.log(Object.values(result))
UPDATE to preserve order
const people = [{name:"Henry",id:"1934",wormsEaten:4},{name:"Melinda",id:"9283",wormsEaten:0},{name:"James",id:"1029",wormsEaten:4},{name:"Charles",id:"7210",wormsEaten:3},{name:"Sasha",id:"4431",wormsEaten:3}]
const result = people.reduce((accumulator, currentPerson) => {
let key = 'wormsEaten' + currentPerson.wormsEaten // to preserve order
accumulator[key] = accumulator[key] || []
accumulator[key].push(currentPerson)
return accumulator
}, {})
console.log(Object.values(result))
Upvotes: 2