Skip to main content

intersectionBy

Returns the intersection of two arrays using a transformation function to determine equality. Creates a new array containing elements from the source array that have matching transformed values in the target array. The mapper function is used to extract comparable values from elements in both arrays for intersection comparison.

Signature

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

Parameters

NameTypeDescription
source-First array to find intersection from
target-Second array to compare against
mapper-Function to transform elements into comparable values

Returns

Array of elements from source that form the intersection with target

Examples

Compare objects by ID

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

const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
{ id: 3, name: 'Charlie', role: 'user' }
];

const activeUsers = [
{ id: 1, status: 'active' },
{ id: 3, status: 'active' }
];

const activeUserDetails = intersectionBy(users, activeUsers, user => user.id);
console.log(activeUserDetails);
// [{ id: 1, name: 'Alice', role: 'admin' }, { id: 3, name: 'Charlie', role: 'user' }]

Compare strings by length

const words = ['cat', 'dog', 'elephant', 'ant', 'butterfly'];
const lengths = ['car', 'bus']; // length 3

const wordsWithMatchingLength = intersectionBy(words, lengths, word => word.length);
console.log(wordsWithMatchingLength); // ['cat', 'dog', 'ant']

Complex object intersection

interface Product {
id: number;
name: string;
category: string;
price: number;
}

interface Category {
name: string;
isActive: boolean;
}

const products: Product[] = [
{ id: 1, name: 'Laptop', category: 'Electronics', price: 999 },
{ id: 2, name: 'Book', category: 'Education', price: 29 },
{ id: 3, name: 'Phone', category: 'Electronics', price: 699 }
];

const activeCategories: Category[] = [
{ name: 'Electronics', isActive: true },
{ name: 'Sports', isActive: true }
];

// Find products in active categories
const activeProducts = intersectionBy(
products,
activeCategories,
item => 'category' in item ? item.category : item.name
);
console.log(activeProducts); // [{ id: 1, name: 'Laptop', ... }, { id: 3, name: 'Phone', ... }]

Date-based intersection

const events = [
{ name: 'Meeting', date: new Date('2024-01-15') },
{ name: 'Conference', date: new Date('2024-02-20') },
{ name: 'Workshop', date: new Date('2024-03-10') }
];

const holidays = [
{ name: 'Presidents Day', date: new Date('2024-02-20') },
{ name: 'Memorial Day', date: new Date('2024-05-27') }
];

// Find events that fall on holidays
const conflictingEvents = intersectionBy(
events,
holidays,
item => item.date.getTime()
);
console.log(conflictingEvents); // [{ name: 'Conference', date: ... }]

Nested property comparison

const employees = [
{ id: 1, profile: { department: 'Engineering', level: 'Senior' } },
{ id: 2, profile: { department: 'Marketing', level: 'Junior' } },
{ id: 3, profile: { department: 'Engineering', level: 'Junior' } }
];

const departments = [
{ name: 'Engineering', budget: 100000 },
{ name: 'Sales', budget: 80000 }
];

const engineeringEmployees = intersectionBy(
employees,
departments,
item => 'profile' in item ? item.profile.department : item.name
);
console.log(engineeringEmployees);
// [{ id: 1, profile: { department: 'Engineering', ... } }, { id: 3, profile: { department: 'Engineering', ... } }]

Email domain intersection

const users = [
{ name: 'Alice', email: 'alice@company.com' },
{ name: 'Bob', email: 'bob@gmail.com' },
{ name: 'Charlie', email: 'charlie@company.com' }
];

const allowedDomains = ['company.com', 'partner.org'];

const companyUsers = intersectionBy(
users,
allowedDomains,
item => typeof item === 'string' ? item : item.email.split('@')[1]
);
console.log(companyUsers);
// [{ name: 'Alice', email: 'alice@company.com' }, { name: 'Charlie', email: 'charlie@company.com' }]

Working with different types

interface Person { name: string; age: number; }
interface AgeGroup { min: number; max: number; }

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

const targetAges = [25, 35, 55];

const matchingAgePeople = intersectionBy(
people,
targetAges,
item => typeof item === 'number' ? item : item.age
);
console.log(matchingAgePeople); // [{ name: 'Alice', age: 25 }, { name: 'Bob', age: 35 }]

Playground

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

const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
{ id: 3, name: 'Charlie', role: 'user' }
];

const activeUsers = [
{ id: 1, status: 'active' },
{ id: 3, status: 'active' }
];

const activeUserDetails = intersectionBy(users, activeUsers, user => user.id);
console.log(activeUserDetails);
// [{ id: 1, name: 'Alice', role: 'admin' }, { id: 3, name: 'Charlie', role: 'user' }]

Notes

Performance: Uses Set for O(1) average case lookup after mapping target array. Time complexity is O(n + m) where n is source length and m is target 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 target 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.

Duplicate Handling: If the source array contains elements that map to the same value and that value exists in the mapped target array, all matching elements from source will be included in the result.