import { all } from "./all"; import { last } from "./last-first"; export interface BySelector { (t: T): any; } export function unique(arr: T[], ...by: BySelector[]): T[] { if (!arr) { return null; } switch (by.length) { case 0: return uniqueItem(arr); case 1: return uniqueByOne(arr, by[0]); default: return uniqueBySeveral(arr, by); } } function uniqueItem(arr: T[]): T[] { const set = new Set(arr); return Array.from(set); } function uniqueByOne(arr: T[], by: BySelector): T[] { const set = new Set(); return arr.filter(item => { const key = by(item); if (set.has(key)) { return false; } set.add(key); return true; }); } function uniqueBySeveral(arr: T[], by: BySelector[]): T[] { const map = new Map(); return arr.filter(item => { if (isSet(map, by, item)) { return false; } set(map, by, item); return true; }); } function isSet(map: Map, by: BySelector[], item: T): boolean { return all(by, sel => map = map.get(sel(item))); } function set(map: Map, by: BySelector[], item: T): void { const lastSelector = last(by); for (const selector of by) { const key = selector(item); if (selector === lastSelector) { map.set(key, true); return; } let subMap = map.get(key); if (!subMap) { subMap = new Map(); map.set(key, subMap); } map = subMap; } }