uniqueBy
Removes duplicate elements from an array using a transformation function to determine uniqueness. Creates a new array containing elements where no two elements produce the same value when passed through the mapper function. Only the first occurrence of elements with the same mapped value is retained. Uses Map for efficient duplicate detection based on transformed values.
Signature
const uniqueBy: <Type, SubType>(source: Type[], mapper: (item: Type) => SubType) => Type[]
Parameters
| Name | Type | Description |
|---|---|---|
source | - | Source array to remove duplicates from |
mapper | - | Function to extract comparison value from elements |
Returns
Array with duplicates removed based on mapped values
Examples
Remove duplicate objects by ID
import { uniqueBy } from '@winglet/common-utils';
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
{ id: 1, name: 'Alice Updated', email: 'alice.new@example.com' },
{ id: 3, name: 'Charlie', email: 'charlie@example.com' },
{ id: 2, name: 'Bob Smith', email: 'bob.smith@example.com' }
];
const uniqueUsers = uniqueBy(users, user => user.id);
console.log(uniqueUsers);
// [
// { id: 1, name: 'Alice', email: 'alice@example.com' },
// { id: 2, name: 'Bob', email: 'bob@example.com' },
// { id: 3, name: 'Charlie', email: 'charlie@example.com' }
// ]
// Note: First occurrence of each ID is kept
Remove duplicate strings by length
const words = ['cat', 'dog', 'elephant', 'ant', 'butterfly', 'ox'];
const uniqueByLength = uniqueBy(words, word => word.length);
console.log(uniqueByLength); // ['cat', 'elephant', 'butterfly']
// Keeps first word of each unique length: 3, 8, 9
Remove duplicate emails by domain
const emails = [
'alice@company.com',
'bob@gmail.com',
'charlie@company.com',
'diana@yahoo.com',
'eve@gmail.com'
];
const uniqueByDomain = uniqueBy(emails, email => email.split('@')[1]);
console.log(uniqueByDomain);
// ['alice@company.com', 'bob@gmail.com', 'diana@yahoo.com']
// First email from each domain
Complex object deduplication
interface Product {
id: number;
name: string;
category: string;
price: number;
variant: string;
}
const products: Product[] = [
{ id: 1, name: 'iPhone', category: 'Electronics', price: 999, variant: 'red' },
{ id: 2, name: 'MacBook', category: 'Electronics', price: 1299, variant: 'silver' },
{ id: 3, name: 'iPhone', category: 'Electronics', price: 899, variant: 'blue' },
{ id: 4, name: 'iPad', category: 'Electronics', price: 799, variant: 'gold' }
];
// Remove duplicates by product name
const uniqueByName = uniqueBy(products, product => product.name);
console.log(uniqueByName);
// [
// { id: 1, name: 'iPhone', ... },
// { id: 2, name: 'MacBook', ... },
// { id: 4, name: 'iPad', ... }
// ]
// Remove duplicates by category
const uniqueByCategory = uniqueBy(products, product => product.category);
console.log(uniqueByCategory); // [{ id: 1, name: 'iPhone', ... }]
Nested property deduplication
interface Employee {
id: number;
name: string;
department: {
id: number;
name: string;
};
}
const employees: Employee[] = [
{ id: 1, name: 'Alice', department: { id: 10, name: 'Engineering' } },
{ id: 2, name: 'Bob', department: { id: 20, name: 'Marketing' } },
{ id: 3, name: 'Charlie', department: { id: 10, name: 'Engineering' } },
{ id: 4, name: 'Diana', department: { id: 30, name: 'Sales' } }
];
const uniqueByDepartment = uniqueBy(employees, emp => emp.department.id);
console.log(uniqueByDepartment);
// [
// { id: 1, name: 'Alice', department: { id: 10, name: 'Engineering' } },
// { id: 2, name: 'Bob', department: { id: 20, name: 'Marketing' } },
// { id: 4, name: 'Diana', department: { id: 30, name: 'Sales' } }
// ]
Date-based deduplication
interface Event {
id: number;
name: string;
date: Date;
}
const events: Event[] = [
{ id: 1, name: 'Meeting', date: new Date('2024-01-15T10:00:00Z') },
{ id: 2, name: 'Conference', date: new Date('2024-01-15T14:00:00Z') },
{ id: 3, name: 'Workshop', date: new Date('2024-01-16T09:00:00Z') },
{ id: 4, name: 'Training', date: new Date('2024-01-15T16:00:00Z') }
];
// Remove duplicates by date (ignoring time)
const uniqueByDate = uniqueBy(events, event => event.date.toDateString());
console.log(uniqueByDate);
// [
// { id: 1, name: 'Meeting', date: ... }, // Jan 15
// { id: 3, name: 'Workshop', date: ... } // Jan 16
// ]
Numeric transformation
const numbers = [1.1, 1.9, 2.1, 2.8, 3.2, 3.7];
// Remove duplicates by integer part
const uniqueByFloor = uniqueBy(numbers, num => Math.floor(num));
console.log(uniqueByFloor); // [1.1, 2.1, 3.2]
// Remove duplicates by rounded value
const uniqueByRound = uniqueBy(numbers, num => Math.round(num));
console.log(uniqueByRound); // [1.1, 2.1, 3.2]
String transformation
const names = ['Alice', 'ALICE', 'Bob', 'bob', 'Charlie', 'charlie'];
// Case-insensitive deduplication
const uniqueByLowerCase = uniqueBy(names, name => name.toLowerCase());
console.log(uniqueByLowerCase); // ['Alice', 'Bob', 'Charlie']
// Deduplication by first character
const uniqueByFirstChar = uniqueBy(names, name => name[0].toLowerCase());
console.log(uniqueByFirstChar); // ['Alice', 'Bob', 'Charlie']
Complex key generation
interface Order {
id: number;
customerId: number;
productId: number;
quantity: number;
}
const orders: Order[] = [
{ id: 1, customerId: 100, productId: 1, quantity: 2 },
{ id: 2, customerId: 101, productId: 2, quantity: 1 },
{ id: 3, customerId: 100, productId: 1, quantity: 3 }, // Same customer + product
{ id: 4, customerId: 102, productId: 1, quantity: 1 }
];
// Remove duplicate customer-product combinations
const uniqueCustomerProduct = uniqueBy(
orders,
order => `${order.customerId}-${order.productId}`
);
console.log(uniqueCustomerProduct);
// [
// { id: 1, customerId: 100, productId: 1, quantity: 2 },
// { id: 2, customerId: 101, productId: 2, quantity: 1 },
// { id: 4, customerId: 102, productId: 1, quantity: 1 }
// ]
Playground
import { uniqueBy } from '@winglet/common-utils'; const users = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' }, { id: 1, name: 'Alice Updated', email: 'alice.new@example.com' }, { id: 3, name: 'Charlie', email: 'charlie@example.com' }, { id: 2, name: 'Bob Smith', email: 'bob.smith@example.com' } ]; const uniqueUsers = uniqueBy(users, user => user.id); console.log(uniqueUsers); // [ // { id: 1, name: 'Alice', email: 'alice@example.com' }, // { id: 2, name: 'Bob', email: 'bob@example.com' }, // { id: 3, name: 'Charlie', email: 'charlie@example.com' } // ] // Note: First occurrence of each ID is kept
Notes
Performance: Uses Map for O(1) average case lookup of mapped values. Time complexity is O(n) where n is the array length.
Mapper Function: The mapper function is called once for each element. Ensure the mapper function is pure and returns consistent values for the same input.
First Occurrence: When multiple elements map to the same value, only the first occurrence is retained in the result array.
Order Preservation: Maintains the original order of elements from the source array in the result array.
Map Storage: Uses Map to store the first occurrence of each unique mapped value, providing efficient lookup and maintaining insertion order.