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.
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
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.