/** * Return an array equal to the given one, but with duplicate items removed. * @param items The array to deduplicate. Not mutated. * @param getItemId Get the identifier to compare sameness of two items. * Defaults to the item itself, so sameness means strict equality * (`item1 === item2`). * @param merge Callback function to merge duplicate items if desired; the * returned value will take the place of the first item. * @returns A new array. */ export function unique( items: T[], getItemId: (item: T) => any = (item) => item, merge?: (item1: T, item2: T) => T, ): T[] { const seen = new Set(); const newItems: T[] = []; for (const item of items) { const id = getItemId(item); if (id === undefined || !seen.has(id)) { newItems.push(item); seen.add(id); } else if (merge) { const indexOfConflictingItem = newItems.findIndex( (newItem) => getItemId(newItem) === id, ); newItems[indexOfConflictingItem] = merge( newItems[indexOfConflictingItem], item, ); } } return newItems; }