Neues Node.js-Buch
Alle Artikel

Group objects by a nested property

Problem

You have an array of objects and want to group them by a nested property.

Ingredients

  • the reduce() method

Directions

  1. 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'
        }
      }
    ]
  2. … 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;
      }, {});
  3. 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) => {
        ...
      }, {});
  4. … 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;
      }, {});
  5. Voilá, now you can use the groupBy() function to group objects by a nested property. For example to group by the property city of the address property you would simply pass the function person => 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. The groupBy() 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.