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

deepIntersection

Returns a new array containing elements that exist in both arrays, based on deep equality or a specified key. Preserves order of the first array.

TypeScript
Copied!
1/**
2 * Returns a new array containing elements that exist in both arrays,
3 * based on deep equality or a specified key.
4 * Preserves order of the first array.
5 *
6 * @param a - First array.
7 * @param b - Second array.
8 * @param compareBy - Optional comparison strategy: either 'deep' or a string key.
9 */
10export function deepIntersection<T>(
11  a: T[],
12  b: T[],
13  compareBy?: 'deep' | keyof T
14): T[] {
15  if (compareBy === 'deep') {
16    const bSerialized = new Set(b.map(item => JSON.stringify(item)));
17    return a.filter(item => bSerialized.has(JSON.stringify(item)));
18  }
19
20  if (typeof compareBy === 'string') {
21    const bKeys = new Set((b as any[]).map(item => item?.[compareBy]));
22    return a.filter(item => bKeys.has((item as any)?.[compareBy]));
23  }
24
25  // Default shallow comparison
26  const setB = new Set(b);
27  return a.filter(item => setB.has(item));
28}
  • Flexible comparison strategies

    Supports three modes:

    • Deep equality: via JSON.stringify, useful for comparing object structures.

    • Key-based comparison: for comparing by a specific property (e.g. id).

    • Shallow comparison: default behavior for primitives or reference equality.

  • Preserves order from the first array

    Maintains consistency and predictability, which is helpful for rendering lists or UI elements.

  • Non-mutating and immutable

    Does not alter input arrays, promoting safer and more predictable functional code.

  • Generic typing

    Fully typed with T, works with arrays of any type (primitives, objects, etc.).

  • Efficient for most common use cases

    Uses Set for quick lookups and avoids nested loops in all but the deep comparison case.

Tests | Examples

TypeScript
Copied!
1test('deep intersection (default shallow)', () => {
2  expect(deepIntersection([1, 2, 3], [2, 3, 4])).toEqual([2, 3]);
3});
4
5test('deep intersection with objects (deep)', () => {
6  const a = [{ id: 1 }, { id: 2 }];
7  const b = [{ id: 2 }, { id: 3 }];
8  expect(deepIntersection(a, b, 'deep')).toEqual([{ id: 2 }]);
9});
10
11test('intersection by key', () => {
12  const a = [{ id: 1 }, { id: 2 }];
13  const b = [{ id: 2 }, { id: 3 }];
14  expect(deepIntersection(a, b, 'id')).toEqual([{ id: 2 }]);
15});
16
17test('intersection by key with duplicates', () => {
18  const a = [{ id: 2 }, { id: 2 }, { id: 3 }];
19  const b = [{ id: 2 }];
20  expect(deepIntersection(a, b, 'id')).toEqual([{ id: 2 }, { id: 2 }]);
21});
22
23test('deep intersection with no match', () => {
24  expect(
25    deepIntersection([{ a: 1 }], [{ a: 2 }], 'deep')
26  ).toEqual([]);
27});
28
29test('intersection on empty arrays', () => {
30  expect(deepIntersection([], [], 'deep')).toEqual([]);
31});

Common Use Cases

  • Data reconciliation across APIs or state slices

    Helpful when merging or syncing datasets from different sources using key-based comparison.

  • Deduplicating and filtering user-specific or permissioned items

    For example, matching a user’s access list with system resources or UI widgets.

  • UI rendering based on intersection logic

    Filter cards, rows, or visual blocks by matching keys or deeply equal data shapes.

  • Comparison of nested structures (when deep is enabled)

    Useful in testing utilities or detecting overlapping complex configurations/settings.

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