Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

groupByToMap

Groups items in a Map based on a key returned by a callback.

TypeScript
Copied!
1/**
2 * Groups items in a Map based on a key returned by a callback.
3 *
4 * @param arr - Array of items to group.
5 * @param fn - Function that returns a grouping key for each item.
6 * @returns A Map where each key maps to an array of grouped items.
7 */
8export function groupByToMap<T, K>(arr: T[], fn: (item: T) => K): Map<K, T[]> {
9  const result = new Map<K, T[]>();
10  for (const item of arr) {
11    const key = fn(item);
12    if (!result.has(key)) {
13      result.set(key, []);
14    }
15    result.get(key)!.push(item);
16  }
17  return result;
18}
  • Preserves key types

    Unlike groupBy, which uses object keys (restricted to string | number), this version supports any type as a key — including objects, symbols, dates, tuples, etc., thanks to Map.

  • Order-preserving

    Map maintains insertion order of keys, which makes it suitable for situations where predictable iteration order is important.

  • Safe against prototype pollution

    Since Map doesn't inherit from Object.prototype, there's no risk of conflicts with inherited keys like __proto__, constructor, etc.

  • Type-safe and clean API

    Leverages Map<K, T[]> for more flexible and expressive typing, especially useful in strongly typed environments like TypeScript.

Tests | Examples

TypeScript
Copied!
1test('groupByToMap - basic grouping', () => {
2  const words = ['cat', 'car', 'dog', 'duck'];
3  const grouped = groupByToMap(words, word => word[0]);
4  expect(grouped.get('c')).toEqual(['cat', 'car']);
5  expect(grouped.get('d')).toEqual(['dog', 'duck']);
6});
7
8test('groupByToMap - complex keys', () => {
9  const keyA = Symbol('A');
10  const keyB = Symbol('B');
11  const items = [{ t: keyA }, { t: keyB }, { t: keyA }];
12  const grouped = groupByToMap(items, item => item.t);
13
14  expect(grouped.get(keyA)).toEqual([{ t: keyA }, { t: keyA }]);
15  expect(grouped.get(keyB)).toEqual([{ t: keyB }]);
16});
17
18test('groupByToMap - empty input', () => {
19  const result = groupByToMap([], () => 'x');
20  expect(result.size).toBe(0);
21});

Common Use Cases

  • Indexing by complex keys

    Group items by objects, arrays, or other non-primitive identifiers (e.g., [userId, date], or even entire object snapshots).

  • Cache or memoization scenarios

    When using grouped data as part of a cache where keys need to be reliable references.

  • Preserving original group order

    When it's important to retain the order in which groups were encountered (e.g., for UI rendering).

  • Building lookup tables

    Construct fast-access Map-based groupings for downstream operations like filtering, sorting, or aggregation.

  • Safer than plain objects in untrusted environments

    Especially relevant in serverless environments or public APIs where object pollution could be an attack vector.

Codebase: Utilities -> Arrays -> groupByToMap | Yevhen Klymentiev