-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/InseeFrLab/vite-insee-starter
- Loading branch information
Showing
10 changed files
with
369 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
import { MapLike, SetLike, DateLike, ArrayLike } from "./types"; | ||
import { assert } from "tsafe/assert"; | ||
import { is } from "tsafe/is"; | ||
|
||
/** | ||
* Function that perform a in depth comparison of two things of arbitrary type T | ||
* to see if they represent the deepEqual date regardless of object references. | ||
* | ||
* Think of it as JSON.stringify(o1) === JSON.stringify(o2) | ||
* but unlike a test performed with JSON.stringify the order in the property | ||
* have been assigned to an object does not matter and circular references are supported. | ||
* | ||
* | ||
* If takeIntoAccountArraysOrdering === false then | ||
* representsSameData(["a", "b"], ["b", "a"]) will return true. | ||
* | ||
* If Date are compared via .getTime() | ||
* | ||
* The objects can includes Map and Set. | ||
* */ | ||
export const deepEqual = (() => { | ||
function sameRec<T>( | ||
o1: T, | ||
o2: T, | ||
{ takeIntoAccountArraysOrdering }: { takeIntoAccountArraysOrdering: boolean } = { | ||
takeIntoAccountArraysOrdering: true | ||
}, | ||
o1Path: { key: string; obj: any }[], | ||
o2Path: { key: string; obj: any }[], | ||
o1RealRef: T = o1, | ||
o2RealRef: T = o2 | ||
): boolean { | ||
if (Object.is(o1, o2)) { | ||
return true; | ||
} | ||
|
||
{ | ||
const i1 = o1Path.map(({ obj }) => obj).indexOf(o1RealRef); | ||
|
||
if (i1 >= 0) { | ||
const i2 = o2Path.map(({ obj }) => obj).indexOf(o2RealRef); | ||
|
||
if (i1 !== i2) { | ||
return false; | ||
} | ||
|
||
{ | ||
const [a, b] = ([o1Path, o2Path] as const).map(oPath => | ||
oPath.map(({ key }) => key).join("") | ||
); | ||
|
||
return a === b; | ||
} | ||
} | ||
} | ||
|
||
if (!(o1 instanceof Object && o2 instanceof Object)) { | ||
return false; | ||
} | ||
|
||
if (typeof o1 === "function" || typeof o2 === "function") { | ||
return false; | ||
} | ||
|
||
if (DateLike.match(o1)) { | ||
if (!DateLike.match(o2)) { | ||
return false; | ||
} | ||
|
||
return o1.getTime() === o2!.getTime(); | ||
} | ||
|
||
if (MapLike.match<any, any>(o1)) { | ||
if (!MapLike.match<any, any>(o2)) { | ||
return false; | ||
} | ||
|
||
type Entry = { key: any; value: any }; | ||
|
||
const newO1 = new Set<Entry>(); | ||
const newO2 = new Set<Entry>(); | ||
|
||
for (const o of [o1, o2]) { | ||
const newO = o === o1 ? newO1 : newO2; | ||
|
||
const arr = Array.from(o.keys()); | ||
|
||
for (let i = 0; i < arr.length; i++) { | ||
const key = arr[i]; | ||
const value = o.get(key)!; | ||
|
||
newO.add({ key, value }); | ||
} | ||
} | ||
|
||
return sameRec( | ||
newO1, | ||
newO2, | ||
{ takeIntoAccountArraysOrdering }, | ||
o1Path, | ||
o2Path, | ||
o1RealRef as any, | ||
o2RealRef | ||
); | ||
} | ||
|
||
let takeIntoAccountArraysOrderingOv: false | undefined = undefined; | ||
|
||
if (SetLike.match(o1)) { | ||
if (!SetLike.match(o2)) { | ||
return false; | ||
} | ||
|
||
o1 = Array.from(o1.values()) as any; | ||
o2 = Array.from(o2.values()) as any; | ||
|
||
takeIntoAccountArraysOrderingOv = false; | ||
} | ||
|
||
//NOTE: The two following lines shouldn't be necessary... | ||
assert(is<Object>(o1)); | ||
assert(is<Object>(o2)); | ||
|
||
if (ArrayLike.match<any>(o1)) { | ||
if (!ArrayLike.match<any>(o2)) { | ||
return false; | ||
} | ||
|
||
if (o1.length !== o2.length) { | ||
return false; | ||
} | ||
|
||
if ( | ||
!(takeIntoAccountArraysOrderingOv !== undefined | ||
? takeIntoAccountArraysOrderingOv | ||
: takeIntoAccountArraysOrdering) | ||
) { | ||
const o2Set = new Set(Array.from(o2)); | ||
|
||
for (let i = 0; i < o1.length; i++) { | ||
if (!(`${i}` in o1)) { | ||
continue; | ||
} | ||
|
||
const val1 = o1[i]; | ||
|
||
if (o2Set.has(val1)) { | ||
o2Set.delete(val1); | ||
continue; | ||
} | ||
|
||
let isFound = false; | ||
|
||
for (const val2 of o2Set.values()) { | ||
if ( | ||
!sameRec( | ||
val1, | ||
val2, | ||
{ takeIntoAccountArraysOrdering }, | ||
[...o1Path, { obj: o1RealRef, key: "*" }], | ||
[...o2Path, { obj: o2RealRef, key: "*" }] | ||
) | ||
) { | ||
continue; | ||
} | ||
|
||
isFound = true; | ||
o2Set.delete(val2); | ||
break; | ||
} | ||
|
||
if (!isFound) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
//continue | ||
} else if ( | ||
!sameRec( | ||
Object.keys(o1).filter(key => (o1 as any)[key] !== undefined), | ||
Object.keys(o2).filter(key => (o2 as any)[key] !== undefined), | ||
{ takeIntoAccountArraysOrdering: false }, | ||
[], | ||
[] | ||
) | ||
) { | ||
return false; | ||
} | ||
|
||
for (const key in o1) { | ||
if ( | ||
!sameRec( | ||
(o1 as any)[key], | ||
(o2 as any)[key], | ||
{ takeIntoAccountArraysOrdering }, | ||
[...o1Path, { obj: o1RealRef, key }], | ||
[...o2Path, { obj: o2RealRef, key }] | ||
) | ||
) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
return function deepEqual<T>( | ||
o1: T, | ||
o2: T, | ||
{ takeIntoAccountArraysOrdering }: { takeIntoAccountArraysOrdering: boolean } = { | ||
takeIntoAccountArraysOrdering: true | ||
} | ||
): boolean { | ||
return sameRec(o1, o2, { takeIntoAccountArraysOrdering }, [], []); | ||
}; | ||
})(); | ||
|
||
/** | ||
* Return the "deepEqual" function with "takeIntoAccountArraysOrdering" default value set as desired. | ||
* */ | ||
export function deepEqualFactory({ | ||
takeIntoAccountArraysOrdering | ||
}: { | ||
takeIntoAccountArraysOrdering: boolean; | ||
}) { | ||
return { | ||
deepEqual: <T>( | ||
o1: T, | ||
o2: T, | ||
prop: { takeIntoAccountArraysOrdering: boolean } = { takeIntoAccountArraysOrdering } | ||
) => deepEqual(o1, o2, prop) | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
|
||
export function getPrototypeChain(obj: Object, callback?: (proto: Object) => boolean): Object[] { | ||
const proto = Object.getPrototypeOf(obj); | ||
|
||
if (!proto) { | ||
return []; | ||
} | ||
|
||
const doContinue = callback?.(proto); | ||
|
||
if (!doContinue) { | ||
return [proto]; | ||
} | ||
|
||
return [proto, ...getPrototypeChain(proto)]; | ||
} | ||
getPrototypeChain.isMatched = (obj: Object, regExp: RegExp): boolean => { | ||
let out = false; | ||
|
||
getPrototypeChain(obj, ({ constructor }) => { | ||
out = regExp.test(constructor.name); | ||
return !out; | ||
}); | ||
|
||
return out; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./deepEqual"; |
Oops, something went wrong.