import { ReduceArguments, toReduceArguments } from "./reduceify"; import { contains } from "./contains"; import { removeDuplicates } from "./removeDuplicates"; import { id } from "tsafe/id"; export type ArrDiff = { added: readonly T[]; removed: readonly T[]; } const areStrictEqual = (e1: unknown, e2: unknown) => e1 === e2; /** WARNING: Providing areEquals significantly impact performances */ export function arrDiff( arr: readonly ArrOf[], newArr: readonly ArrOf[], areEquals: (e1: ArrOf, e2: ArrOf) => boolean = areStrictEqual ): ArrDiff { const arrDiff = { "added": id([]), "removed": id([]) }; if ( arr.length === newArr.length && arr.every((elem, i) => areEquals(elem, newArr[i])) ) { return arrDiff; } if (areEquals !== areStrictEqual) { return { "added": newArr .reduce(...removeDuplicates(areEquals)) .filter(newEntry => !arr.reduce(...contains(entry => areEquals(entry, newEntry)))), "removed": arr .reduce(...removeDuplicates(areEquals)) .filter(entry => !newArr.reduce(...contains(newEntry => areEquals(newEntry, entry)))) }; } const arrAsSet = new Set(arr); const newArrAsSet = new Set(newArr); arrAsSet.forEach(elem => { if (newArrAsSet.has(elem)) { return; } arrDiff.removed.push(elem); }); newArrAsSet.forEach(elem => { if (arrAsSet.has(elem)) { return; } arrDiff.added.push(elem); }); return arrDiff; } export function diff( newArr: readonly ArrOf[], areEquals?: (e1: ArrOf, e2: ArrOf) => boolean ): ReduceArguments> { return toReduceArguments(arrDiff, newArr, areEquals); } export function diffFactory({ areEquals }: { areEquals: (e1: T, e2: T) => boolean; }) { return { "diff": (newArr: readonly ArrOf[]) => diff(newArr, areEquals) }; }