import {isArray, isNot, isObject} from './predicate';
import {ArraySet, Comparator, ComparatorProducer, Predicate} from './type';
import {subtractBy} from './arrayset';
import {getElForPathIn} from './objectstruct';
// ------------ @author Daniel de Oliveira -----------------
export const tripleEqual: any = (l:A) =>
(r:A) => l === r;
export const is = tripleEqual;
export const jsonEqual: any = (l:A) =>
(r:A) => tripleEqual(JSON.stringify(l))(JSON.stringify(r));
export const differentFromBy: ComparatorProducer = (compare: Comparator) => (a:A) =>
isNot(compare(a));
export const includedInBy = (compare: Comparator) => (as: Array) =>
(a: A): boolean => {
if (!isArray(as)) throw new TypeError('includedInBy: expected an Array');
return includesBy(compare)(as, a).length > 0;
};
export const subsetOfBy = (compare: Comparator) => (superset: Array) =>
(subset: Array): boolean => {
if (!isArray(subset) || !isArray(superset))
throw new TypeError('containedInBy: expected Arrays');
return subset
.filter(includedInBy(compare)(superset))
.length === subset.length;
};
export const supersetOfBy = (compare: Comparator) => (subset: ArraySet) =>
(superset: ArraySet): boolean => subsetOfBy(compare)(superset)(subset);
const compare = (acomparator: Comparator, ocomparator: Comparator) => (l: any) =>
(r: any): boolean => {
// Array
if (isArray(l) && isArray(r)) return acomparator(l)(r);
// {} or Object
if (isObject(l) && isObject(r)) {
if (!samesetBy(undefined as any)(Object.keys(l))(Object.keys(r))) return false;
return ocomparator(l)(r);
}
return l instanceof Object && r instanceof Object
// for example Date, Map
? jsonEqual(l)(r)
// numbers, strings
: typeof l === typeof r && l === r;
};
const c = (acomparator: Comparator, ocomparator: Comparator) => (l: any) =>
(r: any): boolean => compare(acomparator, ocomparator)(l)(r);
export const arrayEqualBy = (objectComparator?: Comparator) =>
(as1: Array) => (as2: Array): boolean => {
const ocmp = objectComparator ? objectComparator : objectEqualBy(arrayEqualBy() as any);
return as1.length !== as2.length
? false
: as1
.filter((a, i) => compare(equalBy(arrayEqualBy() as any) as any, ocmp)(a)(as2[i]))
.length === as2.length;
};
// Compares 2 arrays where elements order does not matter
export const samesetBy: (_: Comparator) => any =
(objectComparator?: Comparator) =>
(as1: Array) =>
(as2: Array) => {
const ocmp = objectComparator ? objectComparator : objectEqualBy(samesetBy(undefined as any));
const acmp = objectComparator ? samesetBy(ocmp): samesetBy(undefined as any);
return subtractBy(c(acmp, ocmp))(as1)(as2).length === 0
&& subtractBy(c(acmp, ocmp))(as2)(as1).length === 0;
};
export const objectEqualBy =
(arrayComparator: Comparator) =>
(o1: Object) =>
(o2: Object): boolean => {
if (!isObject(o1) || !isObject(o2))
throw new TypeError('types do not match objectEqualBy');
if (!samesetBy(undefined as any)(Object.keys(o1))(Object.keys(o2))) return false;
return Object
.keys(o1)
.filter(key => {
return compare(
arrayComparator,
objectEqualBy(arrayComparator))
((o1 as any)[key])
((o2 as any)[key]);
})
.length === Object.keys(o1).length;
};
export const equalBy =
(arrayComparator: Comparator) =>
(o1: any) =>
(o2: any): boolean => compare(arrayComparator,
objectEqualBy(arrayComparator))(o1)(o2);
const onBy = (compare: Function) => (path: string) =>
(l: any) => (r: any) =>
path.length === 0
? undefined
: compare(getElForPathIn(l, path))(getElForPathIn(r, path));
export const on = (path: string, compare: Function = tripleEqual) =>
(l: any) =>
typeof compare(l) === 'function'
? (r: any) => onBy(compare)(path)(l)(r)
: compare(getElForPathIn(l, path));
const includesBy =
(compare: Comparator = tripleEqual) =>
(as: Array, a: A) =>
as.filter(compare(a));
export const by = (p: Predicate) => p;
export const differentFrom = differentFromBy(tripleEqual);
export const includedIn = includedInBy(tripleEqual);
export const subsetOf = subsetOfBy(tripleEqual);
export const supersetOf = supersetOfBy(tripleEqual);
export const arrayEqual = arrayEqualBy(undefined as any);
// Compares 2 arrays where elements order does not matter
export const sameset: Comparator = samesetBy(undefined as any);
export const objectEqual: Comparator = objectEqualBy(arrayEqual as any);
export const equal = equalBy(arrayEqual as any);
export const equalTo = equal;