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

differenceBy

Returns items from array a that are not present in array b. Performs deep comparison, or comparison by a specific key if provided.

TypeScript
Copied!
1/**
2 * Returns items from array 'a' that are not present in array 'b'.
3 * Performs deep comparison, or comparison by a specific key if provided.
4 */
5export function differenceBy<T>(
6  a: T[],
7  b: T[],
8  keyOrFn?: keyof T | ((item: T) => any)
9): T[] {
10  const getKey = typeof keyOrFn === 'function'
11    ? keyOrFn
12    : keyOrFn
13      ? (item: T) => item[keyOrFn]
14      : (item: T) => item;
15
16  const bKeys = new Set(b.map(getKey));
17  return a.filter(item => !bKeys.has(getKey(item)));
18}
  • Supports flexible comparison

    Allows comparison by:

    • A specific key (keyof T)

    • A custom function ((item: T) => any)

    • Full value (default case)

  • Efficient key-based lookup

    Internally uses Set for O(1) lookups, resulting in an overall O(n) performance for typical use cases.

  • Generic and reusable

    Works with any array of objects or primitives due to TypeScript generics and flexible key extraction.

  • Non-mutating and pure

    Always returns a new array without changing the original inputs.

  • Readable and extendable

    Easy to understand logic and flexible structure allows for enhancements like case-insensitive matching or transformation pipelines.

Tests | Examples

TypeScript
Copied!
1test('performs deep comparison when no keyOrFn is provided (primitive values)', () => {
2  expect(differenceBy([1, 2, 3], [2])).toEqual([1, 3]);
3});
4
5test('compares objects by a given key', () => {
6  const a = [{ id: 1 }, { id: 2 }, { id: 3 }];
7  const b = [{ id: 2 }];
8  expect(differenceBy(a, b, 'id')).toEqual([{ id: 1 }, { id: 3 }]);
9});
10
11test('compares objects using a mapping function', () => {
12  const a = [{ name: 'Alice' }, { name: 'Bob' }];
13  const b = [{ name: 'Bob' }];
14  expect(differenceBy(a, b, item => item.name)).toEqual([{ name: 'Alice' }]);
15});
16
17test('returns empty array if all elements are excluded', () => {
18  const a = [{ id: 1 }, { id: 2 }];
19  const b = [{ id: 1 }, { id: 2 }];
20  expect(differenceBy(a, b, 'id')).toEqual([]);
21});
22
23test('returns original array if b is empty', () => {
24  const a = [{ id: 1 }, { id: 2 }];
25  const b: any[] = [];
26  expect(differenceBy(a, b, 'id')).toEqual(a);
27});
28
29test('handles duplicate keys in array A', () => {
30  const a = [{ id: 1 }, { id: 2 }, { id: 2 }];
31  const b = [{ id: 2 }];
32  expect(differenceBy(a, b, 'id')).toEqual([{ id: 1 }]);
33});
34
35test('works with undefined keyOrFn and complex objects (compares by reference)', () => {
36  const obj1 = { a: 1 };
37  const obj2 = { a: 1 };
38  expect(differenceBy([obj1], [obj2])).toEqual([obj1]); // obj1 !== obj2 by reference
39});

Common Use Cases

  • Removing previously matched objects

    Useful in UI filtering (e.g., removing already-displayed suggestions, hidden rows).

  • Data diffing

    Determine what records were removed when comparing old and new states (e.g., users, items, transactions).

  • Selective cleanup

    Remove duplicates or irrelevant entries based on an object property or dynamic condition.

  • Normalization

    Identify items in one dataset that are missing in another, based on ID, slug, or any other distinguishing attribute.

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