Skip to the code
cf.

uniqueBy

Write uniqueBy(array, key). It returns a new array without duplicates, where two items are duplicates when they have the same key. For each key it keeps the first item, and the items stay in their original order.

key is either a function that takes an item and returns its key, or a property name, in which case an item's key is the value of that property.

Keys are compared the way a Set compares values: NaN matches NaN, 0 matches -0, 1 and "1" are different, and objects match only when they're the same object. array itself never changes.

const users = [
  { id: 1, name: "Ada" },
  { id: 2, name: "Lin" },
  { id: 1, name: "Ada L." },
];
uniqueBy(users, "id");                             // [{ id: 1, name: "Ada" }, { id: 2, name: "Lin" }]
uniqueBy([1.2, 1.8, 2.1], Math.floor);             // [1.2, 2.1]
uniqueBy(["a", "B", "A"], (s) => s.toLowerCase()); // ["a", "B"]

What the tests check

  1. removes duplicates by a property name
    const users = [{ id: 1, name: "Ada" }, { id: 2, name: "Lin" }, { id: 1, name: "Ada L." }];
    const result = uniqueBy(users, "id");
    expect(result).toEqual([{ id: 1, name: "Ada" }, { id: 2, name: "Lin" }]);
    expect(result[0]).toBe(users[0]);
    
  2. removes duplicates by a key function
    expect(uniqueBy([1.2, 1.8, 2.1], Math.floor)).toEqual([1.2, 2.1]);
    expect(uniqueBy(["a", "B", "A", "b"], (s) => s.toLowerCase())).toEqual(["a", "B"]);
    
  3. keeps the first item for each key, in the original order
    const items = [{ id: 2, n: "a" }, { id: 1, n: "b" }, { id: 2, n: "c" }, { id: 1, n: "d" }];
    expect(uniqueBy(items, "id").map((item) => item.n)).toEqual(["a", "b"]);
    
  4. compares keys the way a Set does
    expect(uniqueBy([NaN, 0, NaN, -0, 1, "1"], (x) => x)).toEqual([NaN, 0, 1, "1"]);
    
  5. matches object keys only when they're the same object
    const tag = { name: "js" };
    const items = [{ tag }, { tag: { name: "js" } }, { tag }];
    const result = uniqueBy(items, "tag");
    expect(result).toHaveLength(2);
    expect(result[0]).toBe(items[0]);
    expect(result[1]).toBe(items[1]);
    
  6. treats a missing property as the key undefined
    expect(uniqueBy([{ id: 1 }, {}, { name: "x" }], "id")).toEqual([{ id: 1 }, {}]);
    
  7. returns a new array and leaves the original alone
    const input = [1, 1, 2];
    const result = uniqueBy(input, (x) => x);
    expect(result).toEqual([1, 2]);
    expect(input).toEqual([1, 1, 2]);
    const distinct = [1, 2, 3];
    expect(uniqueBy(distinct, (x) => x)).not.toBe(distinct);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).