How to Get Distinct Values from an Array in TypeScript

To get distinct values from an array in TypeScript, pass the array to a Set and spread it back: [...new Set(arr)]. A Set keeps each value once, in first-seen order, and TypeScript keeps the element type, so a string[] stays a string[]:

const distinct = [...new Set(states)];   // ["TX", "CA", "NY", "FL"]

I compiled these snippets with TypeScript 7.0.2 running on Node.js v22.22.2. One of them, the Set-methods example, also needs the ES2025 library turned on.

You’ll see the Set one-liner and the filter alternative, distinct values ignoring case, distinct values of one property with counts, comparing two lists, and how the approaches scale.

Get distinct values with new Set()

Set uses SameValueZero equality, so strings and numbers work as you’d expect. The filter plus indexOf version gives the same result for normal values but not for NaN:

const states: string[] = ["TX", "CA", "TX", "NY", "CA", "FL", "TX"];

const distinct: string[] = [...new Set(states)];
console.log(distinct);
console.log(Array.from(new Set(states)).length, "distinct of", states.length);

const viaFilter = states.filter((s, i, arr) => arr.indexOf(s) === i);
console.log(viaFilter);

const readings = [1, NaN, 2, NaN, 2];
console.log("Set:   ", [...new Set(readings)]);
console.log("filter:", readings.filter((v, i, arr) => arr.indexOf(v) === i));

Output:

[ 'TX', 'CA', 'NY', 'FL' ]
4 distinct of 7
[ 'TX', 'CA', 'NY', 'FL' ]
Set:    [ 1, NaN, 2 ]
filter: [ 1, 2 ]
Command Prompt output of TypeScript new Set and filter with indexOf returning distinct state codes and handling NaN differently
Seven state codes reduce to four distinct ones; Set keeps one NaN, while filter with indexOf drops NaN completely.

indexOf(NaN) is always -1, so every NaN fails the test. Array.from(set) and the spread give the same array; the trade-offs are in converting a Set to an array in TypeScript.

How do I get distinct values ignoring case and spaces?

A Set compares values exactly, so "Austin" and "austin " both survive. Use a normalized key in a Map and keep the first spelling you saw:

const cities = ["Austin", "austin ", "AUSTIN", "Denver", "denver", "Boise"];

const seen = new Map<string, string>();
for (const city of cities) {
  const key = city.trim().toLowerCase();
  if (!seen.has(key)) seen.set(key, city.trim());     // keep the first spelling
}
console.log([...seen.values()]);

Output:

[ 'Austin', 'Denver', 'Boise' ]

trim() and toLowerCase() build the key; the Map value keeps the original text for display. For the case conversion itself, see converting a string to lowercase in TypeScript.

Get distinct values of a property, with counts

For an array of objects, map to the property first, then use a Set. When you also need how often each value appears, count into a Map:

interface Order { id: number; state: string; total: number }

const orders: Order[] = [
  { id: 1, state: "TX", total: 120.0 },
  { id: 2, state: "CA", total: 89.99 },
  { id: 3, state: "TX", total: 45.5 },
  { id: 4, state: "NY", total: 230.25 },
  { id: 5, state: "CA", total: 15.0 },
];

const states = [...new Set(orders.map((o) => o.state))].sort();
console.log("distinct states:", states);

const counts = new Map<string, number>();
for (const o of orders) counts.set(o.state, (counts.get(o.state) ?? 0) + 1);
console.log(counts);
console.log("states with repeat orders:", [...counts].filter(([, n]) => n > 1).map(([s]) => s));

Output:

distinct states: [ 'CA', 'NY', 'TX' ]
Map(3) { 'TX' => 2, 'CA' => 2, 'NY' => 1 }
states with repeat orders: [ 'TX', 'CA' ]
Command Prompt output of TypeScript distinct state values from an array of orders and a Map counting orders per state
The orders give three distinct states in sorted order, the Map counts orders per state, and TX and CA appear as repeat states.

To keep whole objects rather than one field, use the key-based approach in getting unique values from an array of objects.

Compare two arrays with Set union, intersection and difference

ES2025 added set operations to Set. They need --lib es2025 in TypeScript and Node.js 22 or a current browser:

const mondayVisitors = new Set(["emily", "james", "olivia", "liam"]);
const tuesdayVisitors = new Set(["james", "ava", "liam", "noah"]);

console.log("either day:  ", [...mondayVisitors.union(tuesdayVisitors)]);
console.log("both days:   ", [...mondayVisitors.intersection(tuesdayVisitors)]);
console.log("only Monday: ", [...mondayVisitors.difference(tuesdayVisitors)]);
console.log("only one day:", [...mondayVisitors.symmetricDifference(tuesdayVisitors)]);

Output:

either day:   [ 'emily', 'james', 'olivia', 'liam', 'ava', 'noah' ]
both days:    [ 'james', 'liam' ]
only Monday:  [ 'emily', 'olivia' ]
only one day: [ 'emily', 'olivia', 'ava', 'noah' ]
Command Prompt output of TypeScript Set union, intersection, difference and symmetricDifference on two visitor lists
union lists everyone, intersection gives james and liam, and difference shows who came only on Monday.

Before ES2025 you’d write a.filter(x => b.has(x)). To test whether two lists hold exactly the same values, see checking array equality in TypeScript.

Remove duplicates from an array in place

Sometimes other code holds a reference to the same array, so you can’t hand back a new one. Replace the contents in place with splice(), or overwrite and trim with length:

const skus: string[] = ["A-100", "B-220", "A-100", "C-310", "B-220", "D-415"];
const shared = skus;                           // another part of the app holds this reference

skus.splice(0, skus.length, ...new Set(skus));
console.log(skus, "same array:", shared === skus);

const codes = ["TX", "CA", "TX", "NY", "CA", "FL"];
const before = codes;
const seen = new Set<string>();
let write = 0;
for (const c of codes) {
  if (!seen.has(c)) { seen.add(c); codes[write++] = c; }
}
codes.length = write;                         // trim the leftovers
console.log(codes, "same array:", before === codes);

Output:

[ 'A-100', 'B-220', 'C-310', 'D-415' ] same array: true
[ 'TX', 'CA', 'NY', 'FL' ] same array: true
Command Prompt output of TypeScript removing duplicates from an array in place with splice and with a write index, keeping the same reference
Both in-place versions leave four distinct SKUs and print true for the same-array check.

Both keep the first occurrence of each value. The splice arguments are explained in the TypeScript array splice method.

Which distinct method is fastest?

Here’s 50,000 ZIP codes with 5,000 distinct values. filter plus indexOf rescans the array for every element:

const zips: string[] = Array.from({ length: 50_000 }, (_, i) => String(10000 + ((i * 7919) % 5000)));

function time(label: string, fn: () => string[]): void {
  const start = performance.now();
  const out = fn();
  console.log(`${label.padEnd(16)} ${out.length} distinct  ${(performance.now() - start).toFixed(1)} ms`);
}

time("new Set", () => [...new Set(zips)]);
time("filter+indexOf", () => zips.filter((z, i, a) => a.indexOf(z) === i));

Output:

new Set          5000 distinct  1.6 ms
filter+indexOf   5000 distinct  270.5 ms
Command Prompt timing TypeScript new Set against filter with indexOf for distinct values in 50,000 ZIP codes
Both find 5,000 distinct ZIP codes, and new Set finishes much faster than filter with indexOf.

Your timings will differ, but the Set stays roughly linear while indexOf grows with the square of the size. Checking membership the same way is covered in checking if a value exists in an array.

Distinct value methods compared

GoalCode
Distinct primitives[...new Set(arr)]
Old-style, no Setarr.filter((v, i, a) => a.indexOf(v) === i)
Ignore caseMap keyed by v.trim().toLowerCase()
One property[...new Set(arr.map(o => o.state))]
Values with countsMap<value, number>
Dedupe the same arrayarr.splice(0, arr.length, ...new Set(arr))
In both / either arraya.intersection(b), a.union(b)

Next, you could read about these array topics:

Frequently asked questions

How do I get distinct values from an array in TypeScript?

Use [...new Set(arr)] or Array.from(new Set(arr)).

Why does filter with indexOf drop NaN?

indexOf(NaN) returns -1 because NaN never equals itself. A Set treats NaN as one value.

How do I get distinct values case-insensitively?

Key a Map by the lowercased, trimmed value and keep the first original spelling.

How do I remove duplicates from an array without creating a new one?

Use arr.splice(0, arr.length, ...new Set(arr)), which keeps the same array reference.

How do I get distinct values from an array of objects?

Map to the property, then use a Set: [...new Set(orders.map(o => o.state))].

Where is Set documented?

See Set on MDN.