Problem
You have an array of objects and want to group them by a nested property.
Ingredients
- the
reduce()
method
Directions
-
Given: an array of objects (e.g.,
persons
) with nested properties (e.g.,address.city
) …let persons = [ { firstName: 'John', lastName: 'Doe', address: { city: 'London' } }, { firstName: 'Jane', lastName: 'Doe', address: { city: 'Birmingham' } }, { firstName: 'Jane', lastName: 'Smith', address: { city: 'Birmingham' } }, { firstName: 'Dave', lastName: 'Smith', address: { city: 'London' } }, { firstName: 'Jane', lastName: 'Carpenter', address: { city: 'Birmingham' } } ]
-
… and our implementation of the
groupBy()
function from yesterday’s recipe.const groupBy = (array, property) => array.reduce((grouped, object) => { let value = object[property]; grouped[value] = grouped[value] || []; grouped[value].push(object); return grouped; }, {});
-
Change that function to accept a function (
fn
) as second parameter instead of a property name …const groupBy = (array, fn) => // This line changed. array.reduce((grouped, object) => { ... }, {});
-
… and inside the callback of
reduce()
determine the value for the group by calling this function with the particular object.const groupBy = (array, fn) => array.reduce((grouped, object) => { let value = fn(object); // This line changed. grouped[value] = grouped[value] || []; grouped[value].push(object); return grouped; }, {});
-
Voilá, now you can use the
groupBy()
function to group objects by a nested property. For example to group by the propertycity
of theaddress
property you would simply pass the functionperson => person.address.city
as parameter:const groupBy = (array, fn) => array.reduce((grouped, object) => { let value = fn(object); grouped[value] = grouped[value] || []; grouped[value].push(object); return grouped; }, {}); let persons = [ { firstName: 'John', lastName: 'Doe', address: { city: 'London' } }, { firstName: 'Jane', lastName: 'Doe', address: { city: 'Birmingham' } }, { firstName: 'Jane', lastName: 'Smith', address: { city: 'Birmingham' } }, { firstName: 'Dave', lastName: 'Smith', address: { city: 'London' } }, { firstName: 'Jane', lastName: 'Carpenter', address: { city: 'Birmingham' } } ] let groupedByCity = groupBy(persons, person => person.address.city); console.log(groupedByCity); /* looks like this: { "London": [ { "firstName": "John", "lastName": "Doe", "address": { "city": "London" } }, { "firstName": "Dave", "lastName": "Smith", "address": { "city": "London" } } ], "Birmingham": [ { "firstName": "Jane", "lastName": "Doe", "address": { "city": "Birmingham" } }, { "firstName": "Jane", "lastName": "Smith", "address": { "city": "Birmingham" } }, { "firstName": "Jane", "lastName": "Carpenter", "address": { "city": "Birmingham" } } ] } */
Notes
- The
groupBy()
function shown in this recipe only accepts functions. ThegroupBy()
function from yesterday’s recipe only accepts property names and only works for direct properties, not for nested properties. In tomorrow’s recipe we will see how we can combine the functions from both recipes into one function that can do both.