Skip to main content

intersectionWith

Returns the intersection of two arrays using a custom comparison function to determine equality. Creates a new array containing elements from the source array that have matching elements in the target array based on a custom equality comparison. This is useful when you need complex comparison logic that cannot be expressed with simple value transformation.

Signature

const intersectionWith: <Type1, Type2>(source: Type1[], target: Type2[], isEqual: (source: Type1, target: Type2) => boolean) => Type1[]

Parameters

NameTypeDescription
source-First array to find intersection from
target-Second array to compare against
isEqual-Element comparison function to determine equality

Returns

Array of elements from source that form the intersection with target

Examples

Compare objects with custom logic

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

const products = [
{ id: 1, name: 'iPhone', price: 999 },
{ id: 2, name: 'iPad', price: 799 },
{ id: 3, name: 'MacBook', price: 1299 }
];

const wishlist = [
{ id: 1, name: 'iPhone', maxPrice: 1000 },
{ id: 4, name: 'Watch', maxPrice: 400 }
];

// Find products that match wishlist items by name and are within budget
const affordableWishlistItems = intersectionWith(
products,
wishlist,
(product, wish) => product.name === wish.name && product.price <= wish.maxPrice
);
console.log(affordableWishlistItems); // [{ id: 1, name: 'iPhone', price: 999 }]

Case-insensitive string comparison

const availableItems = ['Apple', 'Banana', 'Orange', 'Grape'];
const requestedItems = ['apple', 'BANANA', 'kiwi'];

const matchingItems = intersectionWith(
availableItems,
requestedItems,
(available, requested) => available.toLowerCase() === requested.toLowerCase()
);
console.log(matchingItems); // ['Apple', 'Banana']

Complex object intersection with multiple criteria

interface Employee {
id: number;
name: string;
department: string;
skills: string[];
}

interface JobRequirement {
department: string;
requiredSkills: string[];
minSkillCount: number;
}

const employees: Employee[] = [
{ id: 1, name: 'Alice', department: 'Engineering', skills: ['JavaScript', 'Python', 'React'] },
{ id: 2, name: 'Bob', department: 'Engineering', skills: ['Java', 'Spring'] },
{ id: 3, name: 'Charlie', department: 'Marketing', skills: ['SEO', 'Analytics'] }
];

const jobRequirements: JobRequirement[] = [
{ department: 'Engineering', requiredSkills: ['JavaScript', 'React'], minSkillCount: 2 }
];

const qualifiedEmployees = intersectionWith(
employees,
jobRequirements,
(emp, req) => {
if (emp.department !== req.department) return false;
const matchingSkills = emp.skills.filter(skill => req.requiredSkills.includes(skill));
return matchingSkills.length >= req.minSkillCount;
}
);
console.log(qualifiedEmployees); // [{ id: 1, name: 'Alice', ... }]

Approximate numeric comparison

const measurements = [1.05, 2.48, 3.72, 4.91, 5.15];
const targets = [1.0, 2.5, 5.2];

const closeMatches = intersectionWith(
measurements,
targets,
(measurement, target) => Math.abs(measurement - target) < 0.1
);
console.log(closeMatches); // [1.05, 5.15]

Coordinate intersection within tolerance

interface Point { x: number; y: number; }

const points: Point[] = [
{ x: 1, y: 2 },
{ x: 3, y: 4 },
{ x: 5, y: 6 }
];

const targets: Point[] = [
{ x: 1.1, y: 2.1 },
{ x: 7, y: 8 }
];

const nearbyPoints = intersectionWith(
points,
targets,
(point, target) => {
const distance = Math.sqrt(
Math.pow(point.x - target.x, 2) + Math.pow(point.y - target.y, 2)
);
return distance < 0.5;
}
);
console.log(nearbyPoints); // [{ x: 1, y: 2 }]

Date range intersection

interface Event {
name: string;
startDate: Date;
endDate: Date;
}

interface TimeSlot {
start: Date;
end: Date;
}

const events: Event[] = [
{ name: 'Conference', startDate: new Date('2024-01-10'), endDate: new Date('2024-01-12') },
{ name: 'Workshop', startDate: new Date('2024-01-15'), endDate: new Date('2024-01-16') },
{ name: 'Meeting', startDate: new Date('2024-01-20'), endDate: new Date('2024-01-20') }
];

const availableSlots: TimeSlot[] = [
{ start: new Date('2024-01-11'), end: new Date('2024-01-13') },
{ start: new Date('2024-01-19'), end: new Date('2024-01-21') }
];

// Find events that overlap with available time slots
const overlappingEvents = intersectionWith(
events,
availableSlots,
(event, slot) => event.startDate <= slot.end && event.endDate >= slot.start
);
console.log(overlappingEvents); // [{ name: 'Conference', ... }, { name: 'Meeting', ... }]

Pattern matching

const emails = [
'user@company.com',
'admin@company.com',
'guest@external.org',
'support@company.com'
];

const patterns = [/.*@company\.com$/, /admin.*\/];

const matchingEmails = intersectionWith(
emails,
patterns,
(email, pattern) => pattern.test(email)
);
console.log(matchingEmails); // ['user@company.com', 'admin@company.com', 'support@company.com']

Playground

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

const products = [
{ id: 1, name: 'iPhone', price: 999 },
{ id: 2, name: 'iPad', price: 799 },
{ id: 3, name: 'MacBook', price: 1299 }
];

const wishlist = [
{ id: 1, name: 'iPhone', maxPrice: 1000 },
{ id: 4, name: 'Watch', maxPrice: 400 }
];

// Find products that match wishlist items by name and are within budget
const affordableWishlistItems = intersectionWith(
products,
wishlist,
(product, wish) => product.name === wish.name && product.price <= wish.maxPrice
);
console.log(affordableWishlistItems); // [{ id: 1, name: 'iPhone', price: 999 }]

Notes

Performance: Uses nested loops with O(n×m) time complexity where n is source length and m is target length. Consider using intersectionBy with a hash function for large arrays.

Comparison Function: The isEqual function should be pure and can be asymmetric. It receives elements from source as first parameter and target as second parameter.

Type Safety: Supports different types for source and target arrays as long as the comparison function can handle both types.

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

Early Exit: For each source element, stops checking target elements once a match is found, providing some optimization for cases where matches are found early.