본문으로 건너뛰기

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

NameTypeDescription
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.