본문으로 건너뛰기

differenceBy

Returns elements from the source array that are not present in the exclude array, using a transformation function to determine element equality. Creates a new array containing elements from the source array that do not have matching transformed values in the exclude array. The mapper function is applied to elements from both arrays to extract comparable values.

Signature

const differenceBy: <Type1, Type2>(source: Type1[], exclude: Type2[], mapper: (item: Type1 | Type2) => unknown) => Type1[]

Parameters

NameTypeDescription
source-Source array that serves as the base for comparison
exclude-Array containing elements to exclude from the result
mapper-Function to transform array elements into comparable values

Returns

Array of elements that exist only in the source array

Examples

Compare objects by ID

import { differenceBy } from '@winglet/common-utils';

const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const toRemove = [
{ id: 1, name: 'Alice Updated' },
{ id: 2, name: 'Bob Updated' }
];

const result = differenceBy(users, toRemove, user => user.id);
console.log(result); // [{ id: 3, name: 'Charlie' }]

Compare strings by length

const words = ['apple', 'banana', 'apricot', 'cherry'];
const excludeWords = ['grape', 'lemon']; // length 5

const result = differenceBy(words, excludeWords, word => word.length);
console.log(result); // ['banana', 'apricot', 'cherry'] (apple excluded, length 5)

Complex object comparison

interface User {
id: number;
name: string;
department: string;
role: string;
}

const employees: User[] = [
{ id: 1, name: 'John', department: 'IT', role: 'admin' },
{ id: 2, name: 'Jane', department: 'HR', role: 'user' },
{ id: 3, name: 'Bob', department: 'IT', role: 'admin' }
];

const toExclude: User[] = [
{ id: 1, name: 'John Smith', department: 'IT', role: 'admin' },
{ id: 2, name: 'Jane Doe', department: 'HR', role: 'user' }
];

// Compare by department-role combination
const result = differenceBy(
employees,
toExclude,
user => `${user.department}-${user.role}`
);
console.log(result); // [{ id: 3, name: 'Bob', ... }]

Nested object comparison

const products = [
{ id: 1, details: { category: 'electronics', price: 100 } },
{ id: 2, details: { category: 'books', price: 20 } },
{ id: 3, details: { category: 'electronics', price: 200 } }
];

const excludeProducts = [
{ id: 1, details: { category: 'electronics', price: 150 } }
];

const result = differenceBy(
products,
excludeProducts,
product => product.details.category
);
console.log(result); // [{ id: 2, details: { category: 'books', ... } }]

Working with different types

interface Person { name: string; age: number; }
interface Employee { name: string; age: number; id: number; }

const people: Person[] = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 }
];

const employees: Employee[] = [
{ name: 'Alice', age: 30, id: 1 },
{ name: 'Bob', age: 25, id: 2 }
];

const nonEmployees = differenceBy(people, employees, person => person.name);
console.log(nonEmployees); // [{ name: 'Charlie', age: 35 }]

Playground

import { differenceBy } from '@winglet/common-utils';

const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const toRemove = [
{ id: 1, name: 'Alice Updated' },
{ id: 2, name: 'Bob Updated' }
];

const result = differenceBy(users, toRemove, user => user.id);
console.log(result); // [{ id: 3, name: 'Charlie' }]

Notes

Performance: Uses Set for O(1) average case lookup after mapping exclude array. Time complexity is O(n + m) where n is source length and m is exclude length.

Mapper Function: The mapper function is called once for each element in both arrays. Ensure the mapper function is pure and returns consistent values for the same input.

Type Safety: Supports different types for source and exclude arrays as long as the mapper function can handle both types and returns comparable values.

Order Preservation: Maintains the original order of elements from the source array in the result array.