Free tools Windows power users keep installed
One-click scans. No signup required.
To remove duplicate records, first decide which fields define a duplicate. For records with a stable key such as id, use a Set to track keys while filter() keeps the first matching object. A plain new Set(arrayOfObjects) only removes repeated references; separately created objects with the same fields are still distinct.
Keep the first object for each key
This generic helper accepts a property name from the object type and returns the first object for each distinct value of that property:
As an Amazon Associate I earn from qualifying purchases.
function uniqueBy<T, K extends keyof T>(items: T[], key: K): T[] {
const seen = new Set<T[K]>();
return items.filter((item) => {
const value = item[key];
if (seen.has(value)) return false;
seen.add(value);
return true;
});
}
const users = [
{ id: 1, name: "Ada" },
{ id: 1, name: "Ada Lovelace" },
{ id: 2, name: "Grace" },
];
const uniqueUsers = uniqueBy(users, "id");
// [{ id: 1, name: "Ada" }, { id: 2, name: "Grace" }]
The first object with an ID is retained, so the second record with id: 1 is discarded. The result preserves the order of retained objects and is a new shallow array; neither the original array nor its objects are mutated. Array.prototype.filter() returns a shallow copy containing the elements whose callback result is truthy.
The helper’s type parameter makes key a key of T and makes the Set hold values of that property’s type. It helps catch misspelled or invalid property names at compile time, but the runtime Set and filtering logic—not the type annotation—actually remove duplicates. See the TypeScript Handbook’s mapped types documentation for compile-time key transformations; key remapping with as is available in TypeScript 4.1 and later.
#1 Best Overall
Why a Set of objects does not deduplicate matching fields
JavaScript compares object values in a Set by reference identity. These objects have the same fields but are separate object values, so both remain:
const users = new Set([
{ id: 1, name: "Ada" },
{ id: 1, name: "Ada" },
]);
console.log(users.size); // 2
A Set is useful in the helper because it stores primitive identity keys such as IDs, rather than the record objects themselves. Set equality uses SameValueZero: for example, NaN matches NaN, and 0 matches -0. Set access is specified to have average sublinear complexity, but the JavaScript specification does not guarantee a particular constant-time implementation or runtime.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
Keep the last object when later records should win
If incoming or later records should overwrite earlier records with the same key, use a Map:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallfunction uniqueByLast<T, K extends keyof T>(items: T[], key: K): T[] {
const byKey = new Map<T[K], T>();
for (const item of items) byKey.set(item[key], item);
return [...byKey.values()];
}
Calling set() again for an existing key replaces its value, so the Map contains the last record for each key. Its values are returned in key insertion order: replacing a value does not move that key to the end. If output must follow the order of each key’s last occurrence, implement that ordering rule explicitly rather than assuming this version does it. See MDN’s Map reference.
Deduplicate by a combination of fields
When identity depends on a pair such as accountId and itemId, build a composite key from both values:
const seen = new Set<string>();
const result = rows.filter((row) => {
const composite = JSON.stringify([row.accountId, row.itemId]);
if (seen.has(composite)) return false;
seen.add(composite);
return true;
});
This serialization approach is appropriate only when the components have well-defined JSON-safe primitive values. Do not treat JSON.stringify() as a universal deep-equality function for arbitrary objects: serialization can omit or transform values, depend on property order, and fail to represent application-specific equality. A delimiter-based key such as accountId + ":" + itemId can also collide if a component contains the delimiter. For more complex identities, use a representation or nested Maps that reflects the data model.
Decide how missing keys should behave
With the basic helper, all records whose selected key is undefined are treated as duplicates, so only the first is kept. Decide whether that is right for your data. If records without a key should all remain, add a guard that bypasses deduplication for those records; if they should share a normalized identity, normalize the key deliberately.
Recommended Free Tools
Also choose a policy for nullable or otherwise invalid keys before filtering. The Set compares the actual runtime values it receives, so normalization and validation belong in the application logic when the data rules require them.
Best Value
Choose the approach based on the identity and retention rule
- Same object reference:
new Set(items)removes repeated references, not separately created records with equal fields. - First record per field: use
filter()with a Set of that field’s values. - Last record per field: use a Map keyed by the field and return its values.
- Multiple fields: define a safe composite identity or use nested Maps.
- Structural equality: specify which fields and values matter; there is no universal serialization shortcut that supplies correct deep equality for every data model.
A filter() callback that calls findIndex() or indexOf() can be readable for a small array, but it repeatedly scans prior elements. Tracking keys in a Set avoids that repeated-scan pattern; the exact performance depends on the runtime and data, so do not assume a strict O(1) guarantee for each Set operation.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




