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

groupByMulti

Groups items into multiple buckets based on multiple keys per item. Each item can appear in multiple groups if the key extractor returns an array.

TypeScript
Copied!
1/**
2 * Groups items into multiple buckets based on multiple keys per item.
3 * Each item can appear in multiple groups if the key extractor returns an array.
4 *
5 * @param arr - The array to group.
6 * @param fn - A function that returns one or more keys for each item.
7 * @returns An object where each key maps to an array of grouped items.
8 */
9export function groupByMulti<T, K extends string | number>(
10  arr: T[],
11  fn: (item: T) => K | K[]
12): Record<K, T[]> {
13  return arr.reduce((acc, item) => {
14    const keys = fn(item);
15    const keyList = Array.isArray(keys) ? keys : [keys];
16    for (const key of keyList) {
17      if (!acc[key]) acc[key] = [];
18      acc[key].push(item);
19    }
20    return acc;
21  }, {} as Record<K, T[]>);
22}
  • Multi-key grouping support

    Unlike groupBy, this utility allows each item to be placed into multiple groups by returning an array of keys. This is ideal when items logically belong to several categories.

  • Efficient and concise

    Uses a single reduce loop and minimal branching, ensuring performance remains linear (O(n * m), where m is the average number of keys per item).

  • Flexible key extraction

    Accepts either a single key or multiple keys per item via the extractor function, offering greater versatility in grouping logic.

  • Non-mutating and type-safe

    Produces a new Record<K, T[]> while preserving original data, with full TypeScript type support.

Tests | Examples

TypeScript
Copied!
1test('groupByMulti - group by multiple tags', () => {
2  const posts = [
3    { title: 'Post 1', tags: ['react', 'javascript'] },
4    { title: 'Post 2', tags: ['css', 'design'] },
5    { title: 'Post 3', tags: ['javascript'] },
6  ];
7
8  expect(groupByMulti(posts, post => post.tags)).toEqual({
9    react: [{ title: 'Post 1', tags: ['react', 'javascript'] }],
10    javascript: [
11      { title: 'Post 1', tags: ['react', 'javascript'] },
12      { title: 'Post 3', tags: ['javascript'] },
13    ],
14    css: [{ title: 'Post 2', tags: ['css', 'design'] }],
15    design: [{ title: 'Post 2', tags: ['css', 'design'] }],
16  });
17});
18
19test('groupByMulti - single key fallback', () => {
20  const data = ['apple', 'banana', 'cherry'];
21  expect(groupByMulti(data, word => word[0])).toEqual({
22    a: ['apple'],
23    b: ['banana'],
24    c: ['cherry'],
25  });
26});

Common Use Cases

  • Tag-based categorization

    Group blog posts, files, or products under multiple tags or labels.

  • Access control / role mapping

    Group users or permissions by multiple roles or responsibilities.

  • Search indexing

    Pre-process data for fast lookup across multiple indexed fields or terms.

  • Reverse-mapping datasets

    For example, mapping songs to genres when a song belongs to multiple genres.

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