Skip to content

Instantly share code, notes, and snippets.

@WeeSi
Forked from mikaello/group-objects-by-property.md
Created November 18, 2022 11:24
Show Gist options
  • Select an option

  • Save WeeSi/0d1f0ea204e44746d84c347a3863866c to your computer and use it in GitHub Desktop.

Select an option

Save WeeSi/0d1f0ea204e44746d84c347a3863866c to your computer and use it in GitHub Desktop.
Group Array of JavaScript Objects by Key or Property Value

Group array of JavaScript objects by keys

This fork of JamieMason's implementation changes the key parameter to be an array of keys instead of just a single key. This makes it possible to group by multiple properties instead of just one.

Implementation

const groupBy = keys => array =>
  array.reduce((objectsByKeyValue, obj) => {
    const value = keys.map(key => obj[key]).join('-');
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {});
Click to see TypeScript version
/**
 * Group array of objects by given keys
 * @param keys keys to be grouped by
 * @param array objects to be grouped
 * @returns an object with objects in `array` grouped by `keys`
 * @see <https://gist.github.com/mikaello/06a76bca33e5d79cdd80c162d7774e9c>
 */
const groupBy = <T>(keys: (keyof T)[]) => (array: T[]): Record<string, T[]> =>
  array.reduce((objectsByKeyValue, obj) => {
    const value = keys.map((key) => obj[key]).join('-');
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {} as Record<string, T[]>);

Usage

const cars = [
  { brand: 'Audi', produced: '2016', color: 'black' },
  { brand: 'Audi', produced: '2017', color: 'white' },
  { brand: 'Ford', produced: '2016', color: 'red' },
  { brand: 'Ford', produced: '2016', color: 'white' },
  { brand: 'Peugot', produced: '2018', color: 'white' }
];

const groupByBrand = groupBy(['brand']);
const groupByColor = groupBy(['color']);
const groupByBrandAndYear = groupBy(['brand', 'produced']);

console.log(
  JSON.stringify({
    carsByBrand: groupByBrand(cars),
    carsByColor: groupByColor(cars),
    carsByBrandAndYear: groupByBrandAndYear(cars)
  }, null, 2)
);

Output

{
  "carsByBrand": {
    "Audi": [
      {
        "brand": "Audi",
        "produced": "2016",
        "color": "black"
      },
      {
        "brand": "Audi",
        "produced": "2017",
        "color": "white"
      }
    ],
    "Ford": [
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "red"
      },
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "white"
      }
    ],
    "Peugot": [
      {
        "brand": "Peugot",
        "produced": "2018",
        "color": "white"
      }
    ]
  },
  "carsByColor": {
    "black": [
      {
        "brand": "Audi",
        "produced": "2016",
        "color": "black"
      }
    ],
    "white": [
      {
        "brand": "Audi",
        "produced": "2017",
        "color": "white"
      },
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "white"
      },
      {
        "brand": "Peugot",
        "produced": "2018",
        "color": "white"
      }
    ],
    "red": [
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "red"
      }
    ]
  },
  "carsByBrandAndYear": {
    "Audi-2016": [
      {
        "brand": "Audi",
        "produced": "2016",
        "color": "black"
      }
    ],
    "Audi-2017": [
      {
        "brand": "Audi",
        "produced": "2017",
        "color": "white"
      }
    ],
    "Ford-2016": [
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "red"
      },
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "white"
      }
    ],
    "Peugot-2018": [
      {
        "brand": "Peugot",
        "produced": "2018",
        "color": "white"
      }
    ]
  }
}

See playcode.io for example.

@WeeSi
Copy link
Author

WeeSi commented Nov 18, 2022

  array.reduce((objectsByKeyValue, obj) => {
   // Instead of creating a unique key for each grouped by values, we are now traversing (and building) 
   // the whole object structure for every array value:
    keys.reduce((builder, key, index) => {
      if (index !== keys.length - 1) {
        // Building the nested grouped by structure
        builder[obj[key]] = builder[obj[key]] || {};
      } else {
        // Appending the current object at the leaf node
        builder[obj[key]] = (builder[obj[key]] || []).concat(obj);
      }
      return builder[obj[key]];
    }, objectsByKeyValue);

    return objectsByKeyValue;
  }, {});
JSON.stringify(
  groupBy(["a", "b", "c"])([
    { a: "xxx", b: "yyy", c: "zzz", q: "111" },
    { a: "xxx", b: "yyy", c: "diff" },
    { a: "xxx", b: "yyy", c: "zzz", t: "222" },
  ]),
  null,
  2,
),
);

/*
{
"xxx": {
  "yyy": {
    "zzz": [
      {
        "a": "xxx",
        "b": "yyy",
        "c": "zzz",
        "q": "111"
      },
      {
        "a": "xxx",
        "b": "yyy",
        "c": "zzz",
        "t": "222"
      }
    ],
    "diff": [
      {
        "a": "xxx",
        "b": "yyy",
        "c": "diff"
      }
    ]
  }
}
}
*/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment