Neues Node.js-Buch
Alle Artikel

Group objects by property

Problem

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

Ingredients

  • the reduce() method

Directions

  1. Given: an array of objects (e.g., persons) …

    let persons = [
      {
        firstName: 'John',
        lastName: 'Doe'
      },
      {
        firstName: 'Jane',
        lastName: 'Doe'
      },
      {
        firstName: 'Jane',
        lastName: 'Smith'
      },
      {
        firstName: 'Dave',
        lastName: 'Smith'
      },
      {
        firstName: 'Jane',
        lastName: 'Carpenter'
      }
    ]
  2. … and you want to group those objects by a property (e.g., by firstName).

    let groupedByFirstName = groupBy(persons, 'firstName');
    console.log(groupedByFirstName);
    // should look like this:
    /*
    {
      "John": [
        {
          "firstName": "John",
          "lastName": "Doe"
        }
      ],
      "Jane": [
        {
          "firstName": "Jane",
          "lastName": "Doe"
        },
        {
          "firstName": "Jane",
          "lastName": "Smith"
        },
        {
          "firstName": "Jane",
          "lastName": "Carpenter"
        }
      ],
      "Dave": [
        {
          "firstName": "Dave",
          "lastName": "Smith"
        }
      ]
    }
    */
  3. Create a function named groupBy() that accepts an array and the name of a property (we will use the arrow function style for defining that function).

    const groupBy = (array, property) => {}
  4. Call the reduce() method on the array of objects and pass in an empty object as second parameter ({}). This object will be the result object containing the grouped objects of the array.

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        ...
      }, {});
  5. In the callback function of the reduce() method for every object get the value of the given property.

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        let value = object[property];
        ...
      }, {});
  6. Initialize the array for that value in the result object (grouped) if it wasn’t initialized yet …

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        let value = object[property];
        if(!grouped[value]) {
          grouped[value] = [];
        }
        ...
      }, {});
  7. … which can also be written like this:

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        let value = object[property];
        grouped[value] = grouped[value] || [];
        ...
      }, {});
  8. Push the current object to that array.

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        let value = object[property];
        grouped[value] = grouped[value] || [];
        grouped[value].push(object);
        ...
      }, {});
  9. … and return the result object.

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        let value = object[property];
        grouped[value] = grouped[value] || [];
        grouped[value].push(object);
        return grouped;
      }, {});
  10. Voilá, a tasteful helper function for grouping objects by property.

    const groupBy = (array, property) =>
      array.reduce((grouped, object) => {
        let value = object[property];
        grouped[value] = grouped[value] || [];
        grouped[value].push(object);
        return grouped;
      }, {});
    let persons = [
      {
        firstName: 'John',
        lastName: 'Doe'
      },
      {
        firstName: 'Jane',
        lastName: 'Doe'
      },
      {
        firstName: 'Jane',
        lastName: 'Smith'
      },
      {
        firstName: 'Dave',
        lastName: 'Smith'
      },
      {
        firstName: 'Jane',
        lastName: 'Carpenter'
      }
    ]
    let groupedByFirstName = groupBy(persons, 'firstName');
    console.log(groupedByFirstName);
    /* looks like this:
    {
      "John": [
        {
          "firstName": "John",
          "lastName": "Doe"
        }
      ],
      "Jane": [
        {
          "firstName": "Jane",
          "lastName": "Doe"
        },
        {
          "firstName": "Jane",
          "lastName": "Smith"
        },
        {
          "firstName": "Jane",
          "lastName": "Carpenter"
        }
      ],
      "Dave": [
        {
          "firstName": "Dave",
          "lastName": "Smith"
        }
      ]
    }
    */

Notes

  • The groupBy() function shown in this recipe only works for direct properties, not for nested properties. For example in the following code snippet you cannot group the person objects by city using the current implementation of groupBy().

    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'
        }
      }
    ]
    // this won't work
    let groupedByCity = groupBy(persons, 'address.city');
    console.log(groupedByCity);

Alternative recipes

  • Tomorrow we will see how to further improve the groupBy() function, so that you can group objects by nested properties as well.