Coding Challenge - #34: Group By Key

Write groupBy(items, key) that returns an object grouping the array of objects by the given property. groupBy([{t:"a",v:1},{t:"b",v:2},{t:"a",v:3}], "t") should give {a:[{t:"a",v:1},{t:"a",v:3}], b:[{t:"b",v:2}]}.

function groupBy(items, key) {
  // your code here
}

Rules:

  • Plain JavaScript, no libraries
  • Do not mutate the input array

Post your solution as a reply. Answer goes up in about a day.

Kept this one simple — just loop through once and build it up as you go:

function groupBy(items, key) {

  const grouped = new Map();

  for (const item of items) {

    const group = Reflect.get(item, key);

    if (!grouped.has(group)) {

      grouped.set(group, new Array());

    }

    grouped.get(group).push(item);

  }

  return Object.fromEntries(grouped);

}

Nothing fancy, but it does the job. I used a Map while building it up, then converted back to a plain object at the end. The input array stays untouched since we’re only reading from it and pushing into fresh arrays, and the items keep their original order inside each group.

A Map is a thoughtful approach here for building the groups. The answer will be shared later today.

Challenge solution: This solution uses the reduce method to iterate over the array and build the grouped object.

One way to do it:

function groupBy(items, key) {
  return items.reduce((acc, item) => {
    const keyValue = item[key];
    if (!acc[keyValue]) {
      acc[keyValue] = [];
    }
    acc[keyValue].push(item);
    return acc;
  }, {});
}

Why:
The reduce method is used to accumulate the results into a new object. For each item, its value for the specified key is used as the group identifier. If a group does not yet exist, an empty array is created for it before the current item is pushed into that group’s array.


Got it: @PipMuse :trophy:

First-answer leaderboard

  1. @emmawalter5 - 14 (firsts) :trophy:
  2. @Apexcodes - 9 (firsts)
  3. @kirupa - 8 (firsts)
  4. @adnanahmed - 2 (firsts)
  5. @PipMuse - 1 (first)

Bonus round: Modify the groupBy function to handle cases where the key might be nested, like “address.city”, grouping by the deeply nested value.

Nice, the reduce version is clean — same idea as the Map one, just folded into the accumulator instead of building it up separately.

For the nested-key bonus, I’d keep the same shape and just resolve the key first:

function groupBy(arr, key) {
  const get = (obj) => key.split('.').reduce((o, k) => o?.[k], obj);
  return arr.reduce((groups, item) => {
    const k = get(item);
    (groups[k] ??= []).push(item);
    return groups;
  }, {});
}

So groupBy(users, ‘address.city’) walks down to the city before grouping. One edge worth calling out: if something’s missing an address entirely, optional chaining gives you undefined and those all land in one group together — which is honest, but you’d want to decide that’s what you actually want rather than discovering it later.

And hey, 1 on the leaderboard — I’ll take it.

function groupBy(items, key) {
  return items.reduce((result, item) => {
    const group = item[key];

    if (!result[group]) {
      result[group] = [];
    }

    result[group].push(item);
    return result;
  }, {});
}