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

groupByMultiToMap

Groups items into a Map based on one or more keys returned by a callback. Each item can be placed into multiple groups.

TypeScript
Copied!
1/**
2 * Groups items into a Map based on one or more keys returned by a callback.
3 * Each item can be placed into multiple groups.
4 *
5 * @param arr - Array of items to group.
6 * @param fn - Function that returns an array of grouping keys for each item.
7 * @returns A Map where each key maps to an array of items.
8 */
9export function groupByMultiToMap<T, K>(arr: T[], fn: (item: T) => K[]): Map<K, T[]> {
10  const result = new Map<K, T[]>();
11
12  for (const item of arr) {
13    const keys = fn(item);
14    for (const key of keys) {
15      if (!result.has(key)) {
16        result.set(key, []);
17      }
18      result.get(key)!.push(item);
19    }
20  }
21
22  return result;
23}
  • Supports multiple groupings per item

    Unlike standard grouping functions, this utility allows each item to belong to multiple groups, enabling advanced categorization (e.g., tagging systems, faceted filters).

  • Key type flexibility via Map

    Allows grouping by any key type - including objects, arrays, symbols, or custom types - not limited to strings or numbers as in plain objects.

  • Insertion order preserved

    Map ensures consistent ordering of keys based on the sequence of first appearance, which is helpful for rendering or display purposes.

  • Efficient lookup and storage

    Uses Map for optimized key access and to avoid prototype chain issues inherent in plain object usage.

  • Type-safe and expressive

    Strong TypeScript support makes the utility reliable in typed projects, with explicit handling of key arrays per item.

Tests | Examples

TypeScript
Copied!
1test('groupByMultiToMap - basic usage', () => {
2  const animals = [
3    { name: 'dog', tags: ['mammal', 'pet'] },
4    { name: 'cat', tags: ['mammal', 'pet'] },
5    { name: 'crocodile', tags: ['reptile', 'wild'] }
6  ];
7
8  const grouped = groupByMultiToMap(animals, animal => animal.tags);
9
10  expect(grouped.get('mammal')).toEqual([
11    { name: 'dog', tags: ['mammal', 'pet'] },
12    { name: 'cat', tags: ['mammal', 'pet'] }
13  ]);
14
15  expect(grouped.get('pet')).toEqual([
16    { name: 'dog', tags: ['mammal', 'pet'] },
17    { name: 'cat', tags: ['mammal', 'pet'] }
18  ]);
19
20  expect(grouped.get('reptile')).toEqual([
21    { name: 'crocodile', tags: ['reptile', 'wild'] }
22  ]);
23
24  expect(grouped.get('wild')).toEqual([
25    { name: 'crocodile', tags: ['reptile', 'wild'] }
26  ]);
27});
28
29test('groupByMultiToMap - empty array', () => {
30  const grouped = groupByMultiToMap([], () => ['x']);
31  expect(grouped.size).toBe(0);
32});
33
34test('groupByMultiToMap - empty keys array', () => {
35  const grouped = groupByMultiToMap(['a', 'b'], () => []);
36  expect(grouped.size).toBe(0);
37});

Common Use Cases

  • Tag-based grouping

    Items associated with multiple tags (e.g., blog posts, documents, products) can be grouped by all associated tags simultaneously.

  • Feature faceting in search UIs

    Useful for faceted filters, where a product might fall under several filter categories like color, size, and brand.

  • Permission roles and access mapping

    Users or resources may map to multiple roles or scopes, and this utility helps create role-based collections for further processing.

  • Reverse lookup scenarios

    Build maps where each key is linked to all items that reference it, such as dependencies, references, or relations.

  • Data visualization and analytics

    When preparing grouped datasets for charts or aggregations that must account for overlapping group membership.

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