object-array-utils
Advanced tools
+51
-50
@@ -1,55 +0,56 @@ | ||
| export declare type ObjectLiteral<T = unknown> = Record<string, T>; | ||
| export declare type ObjectInstance<T = unknown> = { | ||
| constructor: Function; | ||
| [key: string]: unknown; | ||
| }; | ||
| export declare type Primitive = string | number | bigint | boolean | symbol | null | undefined; | ||
| declare function isNullOrUndefined(v: unknown): boolean; | ||
| declare function isObject(o: unknown): o is ObjectInstance | ObjectLiteral; | ||
| declare function isObjectLiteral(o: unknown): o is ObjectLiteral; | ||
| declare function isEmptyObjectLiteral(o: unknown): o is {}; | ||
| declare function isObjectInstance(o: unknown): o is ObjectInstance; | ||
| declare function isArray(a: unknown): a is unknown[]; | ||
| export type PlainObject<T = unknown> = Record<string, T>; | ||
| export type Primitive = string | number | bigint | boolean | symbol | null | undefined; | ||
| declare function isPrimitive(v: unknown): v is Primitive; | ||
| declare function isNullOrUndefined(v: unknown): v is null | undefined; | ||
| declare function isPrimitiveWrapper(o: unknown): o is Number | String | Boolean; | ||
| type Unboxed<T> = T extends Number ? number : T extends String ? string : T extends Boolean ? boolean : T; | ||
| declare function unboxPrimitiveWrapper<T>(v: T): Unboxed<T>; | ||
| declare function isEmptyArray(a: unknown): a is []; | ||
| declare function isArrayOfObjects(a: unknown): a is (ObjectInstance | ObjectLiteral)[]; | ||
| declare function isArrayOfObjectLiterals(a: unknown): a is ObjectLiteral[]; | ||
| declare function isArrayOfPrimitives(a: unknown): a is Primitive[]; | ||
| declare function isArrayOfType<T extends string | number | boolean>(a: unknown, type: T extends string ? 'string' : T extends number ? 'number' : 'boolean'): a is Array<T>; | ||
| declare function isArrayWhereEvery<T>(a: unknown, fun: (value: unknown) => boolean): a is Array<T>; | ||
| declare function isObjectLiteralWhereEvery<T>(o: unknown, fun: (value: unknown) => boolean): o is ObjectLiteral<T>; | ||
| declare function areValuesEqual(a: unknown, b: unknown): boolean; | ||
| declare function isPlainObject(o: unknown): o is PlainObject; | ||
| declare function isEmptyPlainObject(o: unknown): o is {}; | ||
| declare function isArrayWhereEvery<T>(a: unknown, predicate: (v: unknown) => v is T): a is T[]; | ||
| declare function isPlainObjectWhereEvery<T>(o: unknown, predicate: (v: unknown) => v is T): o is PlainObject<T>; | ||
| export declare enum ComparisonResult { | ||
| Equal = "EQUAL", | ||
| NotEqual = "NOT_EQUAL", | ||
| DefaultComparison = "DEFAULT_COMPARISON" | ||
| UseDefault = "USE_DEFAULT" | ||
| } | ||
| declare type AreEqualOptions = { | ||
| compare: (a: any, b: any) => ComparisonResult; | ||
| export type ComparePlainObjects = { | ||
| (a: PlainObject, b: PlainObject): ComparisonResult; | ||
| }; | ||
| declare function areObjectsEqual(a: ObjectLiteral | ObjectInstance, b: ObjectLiteral | ObjectInstance, options?: AreEqualOptions): boolean; | ||
| declare function areArraysEqual(a: unknown[], b: unknown[], options?: AreEqualOptions): boolean; | ||
| declare function hasProperty(o: ObjectInstance | ObjectLiteral, prop: string): boolean; | ||
| declare function hasProperties(o: ObjectInstance | ObjectLiteral, props: string[]): boolean; | ||
| declare function filterProperties<T = unknown>(o: object, arg: string[] | ((key: string, val: T) => boolean)): {}; | ||
| declare function rejectProperties<T = unknown>(o: object, arg: string[] | ((key: string, val: T) => boolean)): { | ||
| [k: string]: any; | ||
| export type AreNonPlainObjectsEqual = { | ||
| (a: object, b: object): boolean; | ||
| }; | ||
| declare function takeProperties<T = unknown>(o: object, arg: string[] | ((key: string, val: T) => boolean)): { | ||
| filtered: {}; | ||
| rejected: {}; | ||
| undefined: {}; | ||
| type EqualOptions = { | ||
| comparePlainObjects?: ComparePlainObjects; | ||
| areNonPlainObjectsEqual: AreNonPlainObjectsEqual; | ||
| unboxPrimitives: boolean; | ||
| unorderedArrays: true; | ||
| }; | ||
| declare function areDataEqual(x: unknown, y: unknown, opts: EqualOptions): boolean; | ||
| declare function arePlainObjectsEqual(a: PlainObject, b: PlainObject, options: EqualOptions): boolean; | ||
| declare function areArraysEqual(a: readonly unknown[], b: readonly unknown[], options: EqualOptions): boolean; | ||
| declare function hasProperty(o: object, prop: string): boolean; | ||
| declare function hasProperties(o: object, props: string[]): boolean; | ||
| type KeyPredicate<T> = (key: string, val: T) => boolean; | ||
| export declare function copyOwnStrings<T, K extends string = string>(src: Record<K, T>, filter: (key: K, value: T) => boolean): Record<K, T>; | ||
| declare function pickProperties<T = unknown>(obj: object, arg: string[] | KeyPredicate<T>): Record<string, unknown>; | ||
| declare function omitProperties<T = unknown>(obj: object, arg: string[] | KeyPredicate<T>): Record<string, unknown>; | ||
| declare function partitionProperties<T = unknown>(obj: object, arg: string[] | KeyPredicate<T>): { | ||
| picked: Record<string, unknown>; | ||
| omitted: Record<string, unknown>; | ||
| missingKeys: string[]; | ||
| }; | ||
| declare function removeArrayElements<T = unknown>(array: T[], listOfValues: T[]): T[]; | ||
| declare function removeArrayElement<T = unknown>(array: T[], valueOrFun: T | ((value: T) => boolean)): T[]; | ||
| declare function removeArrayElement<T = unknown>(array: T[], valueOrPredicate: T | ((value: T) => boolean)): T[]; | ||
| declare function removeArrayElementByIndex<T = unknown>(array: T[], index: number): T[]; | ||
| declare function differenceArraysOfPrimitives<T extends Primitive>(a1: T[], a2: T[]): T[]; | ||
| declare function isObjectSubset(superObject: ObjectInstance | ObjectLiteral, subObject: ObjectInstance | ObjectLiteral): boolean; | ||
| declare function isArraySubset(superArray: unknown[], subArray: unknown[]): boolean; | ||
| declare function isPrimitive(value: unknown): boolean; | ||
| declare function cloneShape<T = unknown>(value: T): T; | ||
| declare function deepFreeze(o: unknown[] | ObjectInstance | ObjectLiteral): Readonly<ObjectInstance<unknown> | ObjectLiteral<unknown> | unknown[]>; | ||
| declare function sortProperties(o: ObjectLiteral): { | ||
| [k: string]: unknown; | ||
| }; | ||
| declare type RangeOptionsWithCount = { | ||
| declare function differencePrimitives<T extends Primitive>(a1: readonly T[], a2: readonly T[]): T[]; | ||
| export declare function isDataSubset(sup: unknown, sub: unknown, opts: EqualOptions): boolean; | ||
| declare function isPlainObjectSubset(sup: PlainObject, sub: PlainObject, opt: EqualOptions): boolean; | ||
| declare function isArraySubset(supArr: readonly unknown[], subArr: readonly unknown[], opt: EqualOptions): boolean; | ||
| declare function deepClonePlain<T = unknown>(value: T): T; | ||
| type ObjectOrArray = unknown[] | Record<string | number | symbol, unknown>; | ||
| declare function deepFreezePlain<T extends ObjectOrArray>(root: T): Readonly<T>; | ||
| declare function toSortedObject(o: PlainObject): PlainObject<unknown>; | ||
| type RangeOptionsWithCount = { | ||
| start?: number; | ||
@@ -60,3 +61,3 @@ count: number; | ||
| }; | ||
| declare type RangeOptionsWithEndInclusive = { | ||
| type RangeOptionsWithEndInclusive = { | ||
| start?: number; | ||
@@ -67,3 +68,3 @@ count?: never; | ||
| }; | ||
| declare type RangeOptionsWithEndExclusive = { | ||
| type RangeOptionsWithEndExclusive = { | ||
| start?: number; | ||
@@ -74,6 +75,6 @@ count?: never; | ||
| }; | ||
| declare type RangeOptions = RangeOptionsWithCount | RangeOptionsWithEndInclusive | RangeOptionsWithEndExclusive; | ||
| type RangeOptions = RangeOptionsWithCount | RangeOptionsWithEndInclusive | RangeOptionsWithEndExclusive; | ||
| declare function range(options: RangeOptions): number[]; | ||
| declare function duplicate<T = unknown>(value: T, count: number, transformFun?: (v: T, _i: number) => T): T[]; | ||
| declare function unique<T>(a: T[], fun?: (val: T) => unknown): T[]; | ||
| export { areArraysEqual, areObjectsEqual, areValuesEqual, cloneShape, deepFreeze, differenceArraysOfPrimitives, duplicate, filterProperties, hasProperty, hasProperties, isArray, isArrayOfObjects, isArrayOfObjectLiterals, isArrayOfPrimitives, isArrayOfType, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyObjectLiteral, isNullOrUndefined, isObject, isObjectInstance, isObjectLiteral, isObjectLiteralWhereEvery, isObjectSubset, isPrimitive, range, rejectProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, sortProperties, takeProperties, unique }; | ||
| declare function repeat<T = unknown>(value: T, count: number, transformFun?: (v: T, _i: number) => T): T[]; | ||
| declare function unique<T, K = T>(array: readonly T[], keyFn?: (item: T) => K): T[]; | ||
| export { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, hasProperty, hasProperties, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isNullOrUndefined, isPlainObject, isPlainObjectWhereEvery, isPlainObjectSubset, isPrimitive, isPrimitiveWrapper, range, omitProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unboxPrimitiveWrapper, partitionProperties, unique }; |
+313
-333
@@ -0,84 +1,31 @@ | ||
| function isPrimitive(v) { | ||
| return v == null || (typeof v !== 'object' && typeof v !== 'function'); | ||
| } | ||
| function isNullOrUndefined(v) { | ||
| return v === null || v === undefined; | ||
| return v == null; // matches both null and undefined | ||
| } | ||
| function isObject(o) { | ||
| return isObjectLiteral(o) || isObjectInstance(o); | ||
| function isPrimitiveWrapper(o) { | ||
| return o instanceof Number || o instanceof String || o instanceof Boolean; | ||
| } | ||
| function isObjectLiteral(o) { | ||
| return !isNullOrUndefined(o) && Object.getPrototypeOf(o) === Object.prototype; | ||
| function unboxPrimitiveWrapper(v) { | ||
| return (isPrimitiveWrapper(v) ? v.valueOf() : v); | ||
| } | ||
| function isEmptyObjectLiteral(o) { | ||
| return isObjectLiteral(o) && Object.keys(o).length === 0; | ||
| } | ||
| function isObjectInstance(o) { | ||
| return !isNullOrUndefined(o) | ||
| && !isArray(o) | ||
| && Object.getPrototypeOf(o) !== Object.prototype | ||
| && typeof o === 'object'; | ||
| } | ||
| function isArray(a) { | ||
| return !isNullOrUndefined(a) && Array.isArray(a); | ||
| } | ||
| function isEmptyArray(a) { | ||
| return isArray(a) && a.length === 0; | ||
| return Array.isArray(a) && a.length === 0; | ||
| } | ||
| function isArrayOfObjects(a) { | ||
| if (!isArray(a) || a.length === 0) { | ||
| function isPlainObject(o) { | ||
| if (o == null || typeof o !== 'object') | ||
| return false; | ||
| } | ||
| return !a.some(o => !isObject(o)); | ||
| const proto = Object.getPrototypeOf(o); | ||
| return proto === Object.prototype || proto === null; | ||
| } | ||
| function isArrayOfObjectLiterals(a) { | ||
| if (!isArray(a) || a.length === 0) { | ||
| return false; | ||
| } | ||
| return !a.some(o => !isObjectLiteral(o)); | ||
| function isEmptyPlainObject(o) { | ||
| return isPlainObject(o) && Object.keys(o).length === 0; | ||
| } | ||
| function isArrayOfPrimitives(a) { | ||
| if (!isArray(a) || a.length === 0) { | ||
| return false; | ||
| } | ||
| return !a.some(o => !isPrimitive(o)); | ||
| function isArrayWhereEvery(a, predicate) { | ||
| return Array.isArray(a) && a.every(predicate); | ||
| } | ||
| function isArrayOfType(a, type) { | ||
| if (!isArray(a) || a.length === 0) { | ||
| return false; | ||
| } | ||
| return !a.some(o => typeof o !== type); | ||
| function isPlainObjectWhereEvery(o, predicate) { | ||
| return isPlainObject(o) && Object.values(o).every(predicate); | ||
| } | ||
| function isArrayWhereEvery(a, fun) { | ||
| if (!isArray(a) || a.length === 0) { | ||
| return false; | ||
| } | ||
| return !a.some(o => !fun(o)); | ||
| } | ||
| function isObjectLiteralWhereEvery(o, fun) { | ||
| if (!isObjectLiteral(o) || isEmptyObjectLiteral(o)) { | ||
| return false; | ||
| } | ||
| return isArrayWhereEvery(Object.values(o), fun); | ||
| } | ||
| function areValuesEqual(a, b) { | ||
| if (a === b) | ||
| return true; | ||
| if (!a || !b) | ||
| return false; | ||
| if (typeof a !== typeof b) | ||
| return false; | ||
| if (isArray(a) !== isArray(b)) | ||
| return false; | ||
| if (isArray(a)) { | ||
| if (!areArraysEqual(a, b)) | ||
| return false; | ||
| return true; | ||
| } | ||
| if (isObject(a) !== isObject(b)) | ||
| return false; | ||
| if (isObject(a)) { | ||
| if (!areObjectsEqual(a, b)) | ||
| return false; | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| export var ComparisonResult; | ||
@@ -88,59 +35,45 @@ (function (ComparisonResult) { | ||
| ComparisonResult["NotEqual"] = "NOT_EQUAL"; | ||
| ComparisonResult["DefaultComparison"] = "DEFAULT_COMPARISON"; | ||
| ComparisonResult["UseDefault"] = "USE_DEFAULT"; | ||
| })(ComparisonResult || (ComparisonResult = {})); | ||
| function areObjectsEqual(a, b, options) { | ||
| if (!isObject(a) || !isObject(b)) { | ||
| throw new Error('expected objects'); | ||
| function areDataEqual(x, y, opts) { | ||
| if (opts.unboxPrimitives) { | ||
| x = unboxPrimitiveWrapper(x); | ||
| y = unboxPrimitiveWrapper(y); | ||
| } | ||
| if (a === b) | ||
| if (x === y) | ||
| return true; | ||
| // ensure immutability | ||
| if (isObjectLiteral(a)) { | ||
| a = { ...a }; | ||
| } | ||
| if (isObjectLiteral(b)) { | ||
| b = { ...b }; | ||
| } | ||
| switch (options?.compare(a, b)) { | ||
| case 'DEFAULT_COMPARISON': | ||
| case undefined: | ||
| break; | ||
| case 'EQUAL': | ||
| return true; | ||
| case 'NOT_EQUAL': | ||
| if (Array.isArray(x) && Array.isArray(y)) | ||
| return areArraysEqual(x, y, opts); | ||
| if (isPlainObject(x) && isPlainObject(y)) | ||
| return arePlainObjectsEqual(x, y, opts); | ||
| if (isPrimitive(x) && isPrimitive(y)) | ||
| return Object.is(x, y); | ||
| const protoX = Object.getPrototypeOf(x); | ||
| const protoY = Object.getPrototypeOf(y); | ||
| if (!isPrimitiveWrapper(x) && ![Object.prototype, Array.prototype, null].includes(protoX) && | ||
| !isPrimitiveWrapper(y) && ![Object.prototype, Array.prototype, null].includes(protoY)) { | ||
| if (protoX?.constructor !== protoY?.constructor) | ||
| return false; | ||
| return opts.areNonPlainObjectsEqual(x, y); | ||
| } | ||
| if (isObjectLiteral(a) !== isObjectLiteral(b)) | ||
| return Object.is(x, y); | ||
| } | ||
| function arePlainObjectsEqual(a, b, options) { | ||
| if (!isPlainObject(a) || !isPlainObject(b)) | ||
| throw new Error('expected plain objects'); | ||
| if (a === b) | ||
| return true; | ||
| const res = options.comparePlainObjects?.(a, b); | ||
| if (res === ComparisonResult.Equal) | ||
| return true; | ||
| if (res === ComparisonResult.NotEqual) | ||
| return false; | ||
| if (isObjectInstance(a)) { | ||
| const str = a.toString(); | ||
| return str !== '[object Object]' && str === b.toString(); | ||
| } | ||
| if (Object.keys(a).length !== Object.keys(b).length) | ||
| const keysA = Object.keys(a); | ||
| if (keysA.length !== Object.keys(b).length) | ||
| return false; | ||
| for (let [key, value] of Object.entries(a)) { | ||
| if (!hasProperty(b, key)) | ||
| for (const k of keysA) { | ||
| if (!Object.prototype.hasOwnProperty.call(b, k)) | ||
| return false; | ||
| const b_ = b; | ||
| if (value === b_[key]) | ||
| continue; | ||
| if (!value || !b_[key]) | ||
| if (!areDataEqual(a[k], b[k], options)) | ||
| return false; | ||
| if (typeof value !== typeof b_[key]) | ||
| return false; | ||
| if (isArray(value) !== isArray(b_[key])) | ||
| return false; | ||
| if (isArray(value)) { | ||
| if (!areArraysEqual(value, b_[key], options)) | ||
| return false; | ||
| continue; | ||
| } | ||
| if (isObject(value) !== isObject(b_[key])) | ||
| return false; | ||
| if (isObject(value)) { | ||
| if (!areObjectsEqual(value, b_[key], options)) | ||
| return false; | ||
| continue; | ||
| } | ||
| return false; | ||
| } | ||
@@ -150,49 +83,56 @@ return true; | ||
| function areArraysEqual(a, b, options) { | ||
| if (!isArray(a) || !isArray(b)) { | ||
| if (!Array.isArray(a) || !Array.isArray(b)) | ||
| throw new Error('expected arrays'); | ||
| } | ||
| if (a === b) | ||
| return true; | ||
| // ensure immutability | ||
| a = [...a]; | ||
| b = [...b]; | ||
| switch (options?.compare(a, b)) { | ||
| case 'DEFAULT_COMPARISON': | ||
| case undefined: | ||
| break; | ||
| case 'EQUAL': | ||
| return true; | ||
| case 'NOT_EQUAL': | ||
| return false; | ||
| } | ||
| if (a.length !== b.length) | ||
| return false; | ||
| for (let value of a) { | ||
| if (isArray(value)) { | ||
| const value_ = value; | ||
| const index = b.findIndex(e => isArray(e) && areArraysEqual(e, value_, options)); | ||
| if (index === -1) | ||
| return false; | ||
| b.splice(index, 1); | ||
| continue; | ||
| if (a.length === 0) | ||
| return true; | ||
| // fast-path 1: already aligned | ||
| let allMatchByIndex = true; | ||
| for (let i = 0; i < a.length; ++i) { | ||
| if (!areDataEqual(a[i], b[i], options)) { | ||
| allMatchByIndex = false; | ||
| break; | ||
| } | ||
| if (isObject(value)) { | ||
| const value_ = value; | ||
| const index = b.findIndex(e => isObject(e) && areObjectsEqual(e, value_, options)); | ||
| if (index === -1) | ||
| } | ||
| if (allMatchByIndex) | ||
| return true; | ||
| // fast-path 2: both all-primitive | ||
| let allPrimitives = true; | ||
| for (let i = 0; i < a.length; ++i) { | ||
| if (!isPrimitive(a[i]) || !isPrimitive(b[i])) { | ||
| allPrimitives = false; | ||
| break; | ||
| } | ||
| } | ||
| if (allPrimitives) { | ||
| const multiset = new Map(); | ||
| for (const v of a) | ||
| multiset.set(v, (multiset.get(v) ?? 0) + 1); | ||
| for (const v of b) { | ||
| const n = multiset.get(v); | ||
| if (!n) | ||
| return false; | ||
| b.splice(index, 1); | ||
| continue; | ||
| n === 1 ? multiset.delete(v) : multiset.set(v, n - 1); | ||
| } | ||
| const index = b.findIndex(e => e === value); | ||
| if (index === -1) | ||
| return false; | ||
| b.splice(index, 1); | ||
| return true; | ||
| } | ||
| // fallback: generic O(n²) matcher | ||
| const matched = new Array(b.length).fill(false); | ||
| outer: for (const elemA of a) { | ||
| for (let i = 0; i < b.length; ++i) { | ||
| if (!matched[i] && areDataEqual(elemA, b[i], options)) { | ||
| matched[i] = true; | ||
| continue outer; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| function hasProperty(o, prop) { | ||
| if (!isObject(o)) { | ||
| if (typeof o !== 'object' || o == null) | ||
| throw new Error('expected object'); | ||
| } | ||
| return Object.prototype.hasOwnProperty.call(o, prop); | ||
@@ -203,57 +143,45 @@ } | ||
| } | ||
| function filterProperties(o, arg) { | ||
| return isArray(arg) | ||
| ? filterPropsByWhitelist(o, arg) | ||
| : filterPropsByFun(o, arg); | ||
| export function copyOwnStrings(src, filter) { | ||
| const kept = []; | ||
| for (const key of Object.keys(src)) { | ||
| const value = src[key]; | ||
| if (filter(key, value)) | ||
| kept.push([key, value]); | ||
| } | ||
| if (kept.length === Object.keys(src).length) | ||
| return src; | ||
| return Object.fromEntries(kept); | ||
| } | ||
| function filterPropsByWhitelist(o, props) { | ||
| return props.reduce((newObject, prop) => { | ||
| return (prop in o) | ||
| ? { ...newObject, [prop]: o[prop] } | ||
| : newObject; | ||
| }, {}); | ||
| function pickProperties(obj, arg) { | ||
| return Array.isArray(arg) | ||
| ? copyOwnStrings(obj, k => arg.includes(k)) | ||
| : copyOwnStrings(obj, arg); | ||
| } | ||
| function filterPropsByFun(o, fun) { | ||
| const filteredEntries = Object.entries(o).filter(([key, val]) => fun(key, val)); | ||
| return Object.fromEntries(filteredEntries); | ||
| function omitProperties(obj, arg) { | ||
| return Array.isArray(arg) | ||
| ? copyOwnStrings(obj, k => !arg.includes(k)) | ||
| : copyOwnStrings(obj, (k, v) => !arg(k, v)); | ||
| } | ||
| function rejectProperties(o, arg) { | ||
| return isArray(arg) | ||
| ? rejectPropsByWhitelist(o, arg) | ||
| : rejectPropsByFun(o, arg); | ||
| function partitionProperties(obj, arg) { | ||
| const isWanted = Array.isArray(arg) | ||
| ? (k) => arg.includes(k) | ||
| : (k, v) => arg(k, v); | ||
| const picked = {}; | ||
| const omitted = {}; | ||
| const missingKeys = []; | ||
| for (const k in obj) | ||
| if (Object.prototype.hasOwnProperty.call(obj, k)) { | ||
| const v = obj[k]; | ||
| (isWanted(k, v) ? picked : omitted)[k] = v; | ||
| } | ||
| if (Array.isArray(arg)) { | ||
| for (const k of arg) { | ||
| if (!(k in picked)) | ||
| missingKeys.push(k); | ||
| } | ||
| } | ||
| return { picked, omitted, missingKeys }; | ||
| } | ||
| function rejectPropsByWhitelist(o, props) { | ||
| return Object.keys(o).reduce((newObject, prop) => { | ||
| return (props.includes(prop)) | ||
| ? newObject | ||
| : { ...newObject, [prop]: o[prop] }; | ||
| }, {}); | ||
| } | ||
| function rejectPropsByFun(o, fun) { | ||
| const filteredEntries = Object.entries(o).filter(([key, val]) => !fun(key, val)); | ||
| return Object.fromEntries(filteredEntries); | ||
| } | ||
| function takeProperties(o, arg) { | ||
| return isArray(arg) | ||
| ? takePropsByWhitelist(o, arg) | ||
| : takePropsByFun(o, arg); | ||
| } | ||
| function takePropsByWhitelist(o, props) { | ||
| const keys = Object.keys(o); | ||
| const undefined_ = differenceArraysOfPrimitives(props, keys) | ||
| .reduce((acc, key) => ({ ...acc, [key]: undefined }), {}); | ||
| return keys.reduce(({ filtered, rejected, undefined }, prop) => { | ||
| return (props.includes(prop)) | ||
| ? { filtered: { ...filtered, [prop]: o[prop] }, rejected, undefined } | ||
| : { filtered, rejected: { ...rejected, [prop]: o[prop] }, undefined }; | ||
| }, { filtered: {}, rejected: {}, undefined: undefined_ }); | ||
| } | ||
| function takePropsByFun(o, fun) { | ||
| const filteredKeys = Object.entries(o) | ||
| .filter(([key, val]) => fun(key, val)) | ||
| .map(([key, _]) => key); | ||
| return takePropsByWhitelist(o, filteredKeys); | ||
| } | ||
| function removeArrayElements(array, listOfValues) { | ||
| if (!isArray(array) || !isArray(listOfValues)) { | ||
| if (!Array.isArray(array) || !Array.isArray(listOfValues)) { | ||
| throw new Error('expected array'); | ||
@@ -266,9 +194,9 @@ } | ||
| } | ||
| function removeArrayElement(array, valueOrFun) { | ||
| if (!isArray(array)) { | ||
| function removeArrayElement(array, valueOrPredicate) { | ||
| if (!Array.isArray(array)) { | ||
| throw new Error('expected array'); | ||
| } | ||
| return (typeof valueOrFun === 'function') | ||
| ? removeArrayElementByFun(array, valueOrFun) | ||
| : removeArrayElementByValue(array, valueOrFun); | ||
| return (typeof valueOrPredicate === 'function') | ||
| ? removeArrayElementByPredicate(array, valueOrPredicate) | ||
| : removeArrayElementByValue(array, valueOrPredicate); | ||
| } | ||
@@ -281,17 +209,8 @@ function removeArrayElementByValue(array, value) { | ||
| } | ||
| function removeArrayElementByFun(array, fun) { | ||
| let indexToRemove = null; | ||
| for (let i = 0; i < array.length; ++i) { | ||
| if (fun(array[i])) { | ||
| indexToRemove = i; | ||
| break; | ||
| } | ||
| } | ||
| if (indexToRemove === null) { | ||
| return array; | ||
| } | ||
| return removeArrayElementByIndex(array, indexToRemove); | ||
| function removeArrayElementByPredicate(array, fun) { | ||
| const idx = array.findIndex(fun); | ||
| return idx === -1 ? array : [...array.slice(0, idx), ...array.slice(idx + 1)]; | ||
| } | ||
| function removeArrayElementByIndex(array, index) { | ||
| if (!isArray(array)) { | ||
| if (!Array.isArray(array)) { | ||
| throw new Error('expected array'); | ||
@@ -304,4 +223,5 @@ } | ||
| } | ||
| function differenceArraysOfPrimitives(a1, a2) { | ||
| return a1.filter((e) => !a2.includes(e)); | ||
| function differencePrimitives(a1, a2) { | ||
| const exclude = new Set(a2); | ||
| return a1.filter(v => !exclude.has(v)); | ||
| } | ||
@@ -319,142 +239,202 @@ // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/group | ||
| // } | ||
| function isObjectSubset(superObject, subObject) { | ||
| if (!isObject(superObject) || !isObject(subObject)) { | ||
| throw new Error('expected objects'); | ||
| export function isDataSubset(sup, sub, opts) { | ||
| if (opts.unboxPrimitives) { | ||
| sup = unboxPrimitiveWrapper(sup); | ||
| sub = unboxPrimitiveWrapper(sub); | ||
| } | ||
| if (superObject === subObject) | ||
| if (sup === sub) | ||
| return true; | ||
| if (isObjectLiteral(superObject) !== isObjectLiteral(subObject)) | ||
| return false; | ||
| if (isObjectInstance(superObject)) { | ||
| const str = superObject.toString(); | ||
| return str !== '[object Object]' && str === subObject.toString(); | ||
| if (Array.isArray(sup) && Array.isArray(sub)) | ||
| return isArraySubset(sup, sub, opts); | ||
| if (isPlainObject(sup) && isPlainObject(sub)) | ||
| return isPlainObjectSubset(sup, sub, opts); | ||
| if (isPrimitive(sup) && isPrimitive(sub)) | ||
| return Object.is(sup, sub); | ||
| const protoSup = Object.getPrototypeOf(sup); | ||
| const protoSub = Object.getPrototypeOf(sub); | ||
| if (!isPrimitiveWrapper(sup) && ![Object.prototype, Array.prototype, null].includes(protoSup) && | ||
| !isPrimitiveWrapper(sub) && ![Object.prototype, Array.prototype, null].includes(protoSub)) { | ||
| if (protoSup?.constructor !== protoSub?.constructor) | ||
| return false; | ||
| return opts.areNonPlainObjectsEqual(sup, sub); | ||
| } | ||
| if (Object.keys(superObject).length < Object.keys(subObject).length) | ||
| return Object.is(sup, sub); | ||
| } | ||
| function isPlainObjectSubset(sup, sub, opt) { | ||
| if (!isPlainObject(sup) || !isPlainObject(sub)) | ||
| throw new Error('expected plain objects'); | ||
| if (sup === sub) | ||
| return true; | ||
| const res = opt.comparePlainObjects?.(sup, sub); | ||
| if (res === ComparisonResult.Equal) | ||
| return true; | ||
| if (res === ComparisonResult.NotEqual) | ||
| return false; | ||
| return Object.keys(subObject).every(key => { | ||
| if (!hasProperty(superObject, key)) | ||
| for (const [k, vSub] of Object.entries(sub)) { | ||
| if (!hasProperty(sup, k)) | ||
| return false; | ||
| if (superObject[key] === subObject[key]) | ||
| return true; | ||
| if (!superObject[key] || !subObject[key]) | ||
| if (!isDataSubset(sup[k], vSub, opt)) | ||
| return false; | ||
| if (typeof superObject[key] !== typeof subObject[key]) | ||
| return false; | ||
| if (isObject(superObject[key]) !== isObject(subObject[key])) | ||
| return false; | ||
| if (isObject(superObject[key])) { | ||
| return isObjectSubset(superObject[key], subObject[key]); | ||
| } | ||
| if (isArray(superObject[key]) !== isArray(subObject[key])) | ||
| return false; | ||
| if (isArray(superObject[key])) { | ||
| return isArraySubset(superObject[key], subObject[key]); | ||
| } | ||
| return false; | ||
| }); | ||
| } | ||
| return true; | ||
| } | ||
| function isArraySubset(superArray, subArray) { | ||
| if (!isArray(superArray) || !isArray(subArray)) { | ||
| function isArraySubset(supArr, subArr, opt) { | ||
| if (!Array.isArray(supArr) || !Array.isArray(subArr)) | ||
| throw new Error('expected arrays'); | ||
| } | ||
| if (superArray === subArray) | ||
| if (supArr === subArr) | ||
| return true; | ||
| if (superArray.length < subArray.length) | ||
| if (subArr.length === 0) | ||
| return true; | ||
| if (supArr.length < subArr.length) | ||
| return false; | ||
| superArray = [...superArray]; | ||
| subArray = [...subArray]; | ||
| for (let value of subArray) { | ||
| if (isArray(value)) { | ||
| const value_ = value; | ||
| const index = superArray.findIndex(e => isArray(e) && isArraySubset(e, value_)); | ||
| if (index === -1) | ||
| return false; | ||
| superArray.splice(index, 1); | ||
| continue; | ||
| // fast-path 1: already aligned | ||
| let allMatchByIndex = true; | ||
| for (let i = 0; i < subArr.length; ++i) { | ||
| if (!isDataSubset(supArr[i], subArr[i], opt)) { | ||
| allMatchByIndex = false; | ||
| break; | ||
| } | ||
| if (isObject(value)) { | ||
| const value_ = value; | ||
| const index = superArray.findIndex(e => isObject(e) && isObjectSubset(e, value_)); | ||
| if (index === -1) | ||
| } | ||
| if (allMatchByIndex) | ||
| return true; | ||
| // fast-path 2: both all-primitive | ||
| let primitivesOnly = true; | ||
| // check overlapping prefix | ||
| for (let i = 0; i < subArr.length && primitivesOnly; ++i) { | ||
| primitivesOnly = isPrimitive(subArr[i]) && isPrimitive(supArr[i]); | ||
| } | ||
| // check the tail of supArr only | ||
| for (let i = subArr.length; i < supArr.length && primitivesOnly; ++i) { | ||
| primitivesOnly = isPrimitive(supArr[i]); | ||
| } | ||
| if (primitivesOnly) { | ||
| const multiset = new Map(); | ||
| for (const v of supArr) | ||
| multiset.set(v, (multiset.get(v) ?? 0) + 1); | ||
| for (const v of subArr) { | ||
| const n = multiset.get(v); | ||
| if (!n) | ||
| return false; | ||
| superArray.splice(index, 1); | ||
| continue; | ||
| n === 1 ? multiset.delete(v) : multiset.set(v, n - 1); | ||
| } | ||
| const index = superArray.findIndex(e => e === value); | ||
| if (index === -1) | ||
| return false; | ||
| superArray.splice(index, 1); | ||
| return true; | ||
| } | ||
| // fallback: generic O(n²) matcher | ||
| const matched = new Array(supArr.length).fill(false); | ||
| outer: for (const vSub of subArr) { | ||
| for (let i = 0; i < supArr.length; ++i) { | ||
| if (!matched[i] && isDataSubset(supArr[i], vSub, opt)) { | ||
| matched[i] = true; | ||
| continue outer; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| function isPrimitive(value) { | ||
| return value !== Object(value); | ||
| function deepClonePlain(value) { | ||
| const seen = new WeakMap(); | ||
| const walk = (node) => { | ||
| if (isPrimitive(node) || typeof node === "function") | ||
| return node; | ||
| if (seen.has(node)) | ||
| return seen.get(node); | ||
| if (Array.isArray(node)) { | ||
| const copy = []; | ||
| seen.set(node, copy); | ||
| node.forEach((el, i) => (copy[i] = walk(el))); | ||
| return copy; | ||
| } | ||
| if (isPlainObject(node)) { | ||
| const copy = {}; | ||
| seen.set(node, copy); | ||
| Object.keys(node).forEach((k) => { | ||
| copy[k] = walk(node[k]); | ||
| }); | ||
| return copy; | ||
| } | ||
| return node; | ||
| }; | ||
| return walk(value); | ||
| } | ||
| function cloneShape(value) { | ||
| if (isObjectLiteral(value)) { | ||
| return cloneShapeOfObjectLiteral(value); | ||
| function deepFreezePlain(root) { | ||
| if (!isPlainObject(root) && !Array.isArray(root)) { | ||
| throw new Error('expected plain object or array'); | ||
| } | ||
| if (isArray(value)) { | ||
| return cloneShapeOfArray(value); | ||
| } | ||
| return value; | ||
| } | ||
| function cloneShapeOfObjectLiteral(o) { | ||
| return Object.fromEntries(Object.entries(o).map(([key, val]) => [key, cloneShape(val)])); | ||
| } | ||
| function cloneShapeOfArray(a) { | ||
| return a.map((e) => cloneShape(e)); | ||
| } | ||
| function deepFreeze(o) { | ||
| if (!isObject(o) && !isArray(o)) { | ||
| throw new Error('expected object or array'); | ||
| } | ||
| Object.keys(o).forEach((prop) => { | ||
| const o_ = o; | ||
| if ((!isObject(o_[prop]) || !isArray(o_[prop])) && !Object.isFrozen(o_[prop])) { | ||
| deepFreeze(o_[prop]); | ||
| const seen = new WeakSet(); | ||
| const walk = (node) => { | ||
| if ((!isPlainObject(node) && !Array.isArray(node)) || seen.has(node)) | ||
| return; | ||
| seen.add(node); | ||
| for (const key of Object.keys(node)) { | ||
| walk(node[key]); | ||
| } | ||
| }); | ||
| return Object.freeze(o); | ||
| Object.freeze(node); | ||
| }; | ||
| walk(root); | ||
| return root; | ||
| } | ||
| function sortProperties(o) { | ||
| return Object.fromEntries(Object.entries(o).sort(([k1], [k2]) => k1 < k2 ? -1 : 1)); | ||
| function toSortedObject(o) { | ||
| const out = {}; | ||
| for (const k of Object.keys(o).sort()) | ||
| out[k] = o[k]; | ||
| return out; | ||
| } | ||
| function range(options) { | ||
| const unknownOptions = rejectProperties(options, ['start', 'count', 'endInclusive', 'endExclusive']); | ||
| if (!isEmptyObjectLiteral(unknownOptions)) { | ||
| throw new Error(`unknown options: ${Object.keys(unknownOptions).join(', ')}`); | ||
| const unknown = omitProperties(options, [ | ||
| 'start', | ||
| 'count', | ||
| 'endInclusive', | ||
| 'endExclusive', | ||
| ]); | ||
| if (!isEmptyPlainObject(unknown)) { | ||
| throw new Error(`unknown option(s): ${Object.keys(unknown).join(', ')}`); | ||
| } | ||
| if (!options.endInclusive && !options.endExclusive && !options.count) { | ||
| throw new Error('expected either `endInclusive`, `endExclusive` or `count` to be specified'); | ||
| const { start: rawStart, count, endInclusive, endExclusive } = options; | ||
| const start = rawStart ?? 0; | ||
| const hasCount = count !== undefined; | ||
| const hasEndInclusive = endInclusive !== undefined; | ||
| const hasEndExclusive = endExclusive !== undefined; | ||
| const provided = Number(hasCount) + Number(hasEndInclusive) + Number(hasEndExclusive); | ||
| if (provided === 0) { | ||
| throw new Error('Specify either `count`, `endInclusive`, or `endExclusive`.'); | ||
| } | ||
| if (Number(!!options?.count) + Number(!!options?.endInclusive) + Number(!!options?.endExclusive) > 1) { | ||
| throw new Error('expected only one of the properties `endInclusive`, `endExclusive`, or `count` to be specified.'); | ||
| if (provided > 1) { | ||
| throw new Error('Only one of `count`, `endInclusive`, or `endExclusive` may be supplied.'); | ||
| } | ||
| const start = options.start ?? 0; | ||
| if (options.endInclusive && start > options.endInclusive) { | ||
| throw new Error('`endInclusive` should be greater or equal than `start`'); | ||
| let len; | ||
| if (hasCount) { | ||
| if (count < 0) | ||
| throw new Error('`count` must be non-negative'); | ||
| len = count; | ||
| } | ||
| if (options.endExclusive && start >= options.endExclusive) { | ||
| throw new Error('`endExclusive` should be greater than `start`'); | ||
| else if (hasEndInclusive) { | ||
| if (endInclusive < start) | ||
| throw new Error('`endInclusive` must be ≥ `start`'); | ||
| len = endInclusive - start + 1; | ||
| } | ||
| const count = options.count ?? ((options.endInclusive) ? options.endInclusive - start + 1 : options.endExclusive - start); | ||
| return [...Array(count).keys()].map(i => i + start); | ||
| else { // endExclusive | ||
| if (endExclusive <= start) | ||
| throw new Error('`endExclusive` must be > `start`'); | ||
| len = endExclusive - start; | ||
| } | ||
| return Array.from({ length: len }, (_, i) => start + i); | ||
| } | ||
| function duplicate(value, count, transformFun = (v, _i) => v) { | ||
| function repeat(value, count, transformFun = (v, _i) => v) { | ||
| return [...Array(count).keys()].map(i => transformFun(value, i)); | ||
| } | ||
| function unique(a, fun) { | ||
| if (!fun) { | ||
| return [...new Set(a)]; | ||
| function unique(array, keyFn = (x => x)) { | ||
| if (array.length < 2) | ||
| return array; | ||
| const seen = new Set(); | ||
| const result = []; | ||
| for (const item of array) { | ||
| const key = keyFn(item); | ||
| if (seen.has(key)) | ||
| continue; | ||
| seen.add(key); | ||
| result.push(item); | ||
| } | ||
| const uniqueMap = new Map(); | ||
| for (const item of a) { | ||
| const key = fun(item); | ||
| if (!uniqueMap.has(key)) { | ||
| uniqueMap.set(key, item); | ||
| } | ||
| } | ||
| return Array.from(uniqueMap.values()); | ||
| return result.length === array.length ? array : result; | ||
| } | ||
| export { areArraysEqual, areObjectsEqual, areValuesEqual, cloneShape, deepFreeze, differenceArraysOfPrimitives, duplicate, filterProperties, hasProperty, hasProperties, isArray, isArrayOfObjects, isArrayOfObjectLiterals, isArrayOfPrimitives, isArrayOfType, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyObjectLiteral, isNullOrUndefined, isObject, isObjectInstance, isObjectLiteral, isObjectLiteralWhereEvery, isObjectSubset, isPrimitive, range, rejectProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, sortProperties, takeProperties, unique }; | ||
| export { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, hasProperty, hasProperties, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isNullOrUndefined, isPlainObject, isPlainObjectWhereEvery, isPlainObjectSubset, isPrimitive, isPrimitiveWrapper, range, omitProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unboxPrimitiveWrapper, partitionProperties, unique }; |
| export {}; |
+133
-94
| import { expect, test } from 'vitest'; | ||
| import { areArraysEqual, areObjectsEqual, areValuesEqual, cloneShape, deepFreeze, differenceArraysOfPrimitives, duplicate, filterProperties, hasProperties, isArrayOfPrimitives, isArrayOfType, isArrayWhereEvery, isEmptyArray, isEmptyObjectLiteral, isObjectLiteral, isObjectLiteralWhereEvery, isPrimitive, range, rejectProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, sortProperties, takeProperties, unique } from './index'; | ||
| test('areValuesEqual', () => { | ||
| expect(areValuesEqual(null, null)).toBeTruthy(); | ||
| expect(areValuesEqual(null, undefined)).toBeFalsy(); | ||
| expect(areValuesEqual(undefined, undefined)).toBeTruthy(); | ||
| expect(areValuesEqual(false, false)).toBeTruthy(); | ||
| expect(areValuesEqual(1, 1)).toBeTruthy(); | ||
| expect(areValuesEqual(1, '1')).toBeFalsy(); | ||
| expect(areValuesEqual(new Date(), new Date())).toBeTruthy(); | ||
| expect(areValuesEqual([{}], [{}])).toBeTruthy(); | ||
| import { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, omitProperties, partitionProperties, hasProperties, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isPlainObject, isPlainObjectWhereEvery, isPrimitive, isPrimitiveWrapper, unboxPrimitiveWrapper, range, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unique } from './index'; | ||
| test('areDataEqual', () => { | ||
| const areNonPlainObjectsEqual = () => { throw new Error(); }; | ||
| expect(areDataEqual(null, null, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual(null, undefined, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeFalsy(); | ||
| expect(areDataEqual(undefined, undefined, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual(false, false, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual(1, 1, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual(1, '1', { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeFalsy(); | ||
| expect(areDataEqual([{}], [{}], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual(0, -0, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual([0], [-0], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areDataEqual({ foo: 0 }, { foo: -0 }, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| }); | ||
| test('areObjectsEqual', () => { | ||
| expect(areObjectsEqual({ b: null, c: { d: 1, e: "world" }, a: "foo" }, { a: "foo", b: null, c: { e: "world", d: 1 } })).toBeTruthy(); | ||
| expect(areObjectsEqual({ b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }, { a: "foo", b: null, c: { e: "world", d: [{ d: 4, c: 3 }, 1, 0, 2, 5] } })).toBeTruthy(); | ||
| test('areDataEqual containing object instances', () => { | ||
| const areNonPlainObjectsEqual = ((o1, o2) => { | ||
| if (o1 instanceof Date && o2 instanceof Date) { | ||
| return o1.getTime() === o2.getTime(); | ||
| } | ||
| throw new Error(); | ||
| }); | ||
| expect(areDataEqual(new Date(), new Date(), { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| }); | ||
| test('areObjectsEquivalent', () => { | ||
| const areNonPlainObjectsEqual = () => { throw new Error(); }; | ||
| expect(arePlainObjectsEqual({ b: null, c: { d: 1, e: "world" }, a: "foo" }, { a: "foo", b: null, c: { e: "world", d: 1 } }, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(arePlainObjectsEqual({ b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }, { a: "foo", b: null, c: { e: "world", d: [{ d: 4, c: 3 }, 1, 0, 2, 5] } }, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(arePlainObjectsEqual({ foo: 0 }, { foo: -0 }, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| }); | ||
| test('areArraysEqual', () => { | ||
| expect(areArraysEqual([2, 1, 1], [2, 1, 2])).toBeFalsy(); | ||
| expect(areArraysEqual([{ b: null, c: { d: 1, e: "world" }, a: "foo" }, "foo"], ["foo", { a: "foo", b: null, c: { e: "world", d: 1 } }])).toBeTruthy(); | ||
| expect(areArraysEqual([1, { b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }], [{ a: "foo", b: null, c: { e: "world", d: [{ d: 4, c: 3 }, 1, 0, 2, 5] } }, 1])).toBeTruthy(); | ||
| const areNonPlainObjectsEqual = () => { throw new Error(); }; | ||
| expect(areArraysEqual([2, 1, 1], [2, 1, 2], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeFalsy(); | ||
| expect(areArraysEqual([{ b: null, c: { d: 1, e: "world" }, a: "foo" }, "foo"], ["foo", { a: "foo", b: null, c: { e: "world", d: 1 } }], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areArraysEqual([1, { b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }], [{ a: "foo", b: null, c: { e: "world", d: [{ d: 4, c: 3 }, 1, 0, 2, 5] } }, 1], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areArraysEqual([0], [-0], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| }); | ||
| test('areArraysEqual containing object instances', () => { | ||
| const date1 = new Date('December 17, 1995 03:24:00'); | ||
| const date2 = new Date('December 17, 1995 03:24:00'); | ||
| const date3 = new Date('December 18, 1995 03:24:00'); | ||
| expect(areObjectsEqual({ date: date1, dates: [date3, date2, date1] }, { date: date2, dates: [date1, date3, date2] })).toBeTruthy(); | ||
| expect(areArraysEqual([1, 1, 2], [1, 1, 2])).toBeTruthy(); | ||
| deepFreeze([1, { b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }]); | ||
| const areNonPlainObjectsEqual = ((o1, o2) => { | ||
| if (o1 instanceof Date && o2 instanceof Date) { | ||
| return o1.getTime() === o2.getTime(); | ||
| } | ||
| throw new Error(); | ||
| }); | ||
| expect(arePlainObjectsEqual({ date: date1, dates: [date3, date2, date1] }, { date: date2, dates: [date1, date3, date2] }, { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| expect(areArraysEqual([1, 1, 2], [1, 1, 2], { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true })).toBeTruthy(); | ||
| deepFreezePlain([1, { b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }]); | ||
| }); | ||
@@ -35,36 +59,36 @@ test('hasProperties', () => { | ||
| }); | ||
| test('filterProperties using whitelist of props', () => { | ||
| expect(filterProperties({ foo: 1, bar: 2 }, ['foo'])).toEqual({ foo: 1 }); | ||
| expect(filterProperties({ foo: 1, bar: 2 }, ['bar'])).toEqual({ bar: 2 }); | ||
| expect(filterProperties({ foo: 1, bar: 2 }, ['bar', 'foo'])).toEqual({ foo: 1, bar: 2 }); | ||
| expect(filterProperties({ foo: 1, bar: 2 }, ['bar', 'foo', 'baz'])).toEqual({ foo: 1, bar: 2 }); | ||
| expect(filterProperties({ foo: 1, bar: 2 }, ['foo', 'foo'])).toEqual({ foo: 1 }); | ||
| test('pickProperties using whitelist of props', () => { | ||
| expect(pickProperties({ foo: 1, bar: 2 }, ['foo'])).toEqual({ foo: 1 }); | ||
| expect(pickProperties({ foo: 1, bar: 2 }, ['bar'])).toEqual({ bar: 2 }); | ||
| expect(pickProperties({ foo: 1, bar: 2 }, ['bar', 'foo'])).toEqual({ foo: 1, bar: 2 }); | ||
| expect(pickProperties({ foo: 1, bar: 2 }, ['bar', 'foo', 'baz'])).toEqual({ foo: 1, bar: 2 }); | ||
| expect(pickProperties({ foo: 1, bar: 2 }, ['foo', 'foo'])).toEqual({ foo: 1 }); | ||
| }); | ||
| test('filterProperties using function', () => { | ||
| expect(filterProperties({ foo: 1, bar: 2 }, (_key, val) => val < 2)).toEqual({ foo: 1 }); | ||
| expect(filterProperties({ foo: 3, bar: 2 }, (_key, val) => val < 2)).toEqual({}); | ||
| test('pickProperties using function', () => { | ||
| expect(pickProperties({ foo: 1, bar: 2 }, (_key, val) => val < 2)).toEqual({ foo: 1 }); | ||
| expect(pickProperties({ foo: 3, bar: 2 }, (_key, val) => val < 2)).toEqual({}); | ||
| }); | ||
| test('rejectProperties using whitelist of props', () => { | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, ['foo'])).toEqual({ bar: 2 }); | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, ['bar'])).toEqual({ foo: 1 }); | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, ['bar', 'foo'])).toEqual({}); | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, ['bar', 'foo', 'baz'])).toEqual({}); | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, ['foo', 'foo'])).toEqual({ bar: 2 }); | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, ['baz'])).toEqual({ foo: 1, bar: 2 }); | ||
| test('omitProperties using whitelist of props', () => { | ||
| expect(omitProperties({ foo: 1, bar: 2 }, ['foo'])).toEqual({ bar: 2 }); | ||
| expect(omitProperties({ foo: 1, bar: 2 }, ['bar'])).toEqual({ foo: 1 }); | ||
| expect(omitProperties({ foo: 1, bar: 2 }, ['bar', 'foo'])).toEqual({}); | ||
| expect(omitProperties({ foo: 1, bar: 2 }, ['bar', 'foo', 'baz'])).toEqual({}); | ||
| expect(omitProperties({ foo: 1, bar: 2 }, ['foo', 'foo'])).toEqual({ bar: 2 }); | ||
| expect(omitProperties({ foo: 1, bar: 2 }, ['baz'])).toEqual({ foo: 1, bar: 2 }); | ||
| }); | ||
| test('rejectProperties using function', () => { | ||
| expect(rejectProperties({ foo: 1, bar: 2 }, (_key, val) => val < 2)).toEqual({ bar: 2 }); | ||
| expect(rejectProperties({ foo: 3, bar: 2 }, (_key, val) => val < 2)).toEqual({ foo: 3, bar: 2 }); | ||
| expect(rejectProperties({ foo: 3, bar: 2 }, (_key, val) => val > 1)).toEqual({}); | ||
| test('omitProperties using function', () => { | ||
| expect(omitProperties({ foo: 1, bar: 2 }, (_key, val) => val < 2)).toEqual({ bar: 2 }); | ||
| expect(omitProperties({ foo: 3, bar: 2 }, (_key, val) => val < 2)).toEqual({ foo: 3, bar: 2 }); | ||
| expect(omitProperties({ foo: 3, bar: 2 }, (_key, val) => val > 1)).toEqual({}); | ||
| }); | ||
| test('takeProperties using whitelist of props', () => { | ||
| expect(takeProperties({ foo: 1, bar: 2 }, ['foo'])).toEqual({ filtered: { foo: 1 }, rejected: { bar: 2 }, undefined: {} }); | ||
| expect(takeProperties({ foo: 1, bar: 2 }, ['bar'])).toEqual({ filtered: { bar: 2 }, rejected: { foo: 1 }, undefined: {} }); | ||
| expect(takeProperties({ foo: 1, bar: 2 }, ['bar', 'foo'])).toEqual({ filtered: { foo: 1, bar: 2 }, rejected: {}, undefined: {} }); | ||
| expect(takeProperties({ foo: 1, bar: 2 }, ['bar', 'foo', 'baz'])).toEqual({ filtered: { foo: 1, bar: 2 }, rejected: {}, undefined: { baz: undefined } }); | ||
| expect(takeProperties({ foo: 1, bar: 2 }, ['baz'])).toEqual({ filtered: {}, rejected: { foo: 1, bar: 2 }, undefined: { baz: undefined } }); | ||
| test('partitionProperties using whitelist of props', () => { | ||
| expect(partitionProperties({ foo: 1, bar: 2 }, ['foo'])).toEqual({ picked: { foo: 1 }, omitted: { bar: 2 }, missingKeys: [] }); | ||
| expect(partitionProperties({ foo: 1, bar: 2 }, ['bar'])).toEqual({ picked: { bar: 2 }, omitted: { foo: 1 }, missingKeys: [] }); | ||
| expect(partitionProperties({ foo: 1, bar: 2 }, ['bar', 'foo'])).toEqual({ picked: { foo: 1, bar: 2 }, omitted: {}, missingKeys: [] }); | ||
| expect(partitionProperties({ foo: 1, bar: 2 }, ['bar', 'foo', 'baz'])).toEqual({ picked: { foo: 1, bar: 2 }, omitted: {}, missingKeys: ['baz'] }); | ||
| expect(partitionProperties({ foo: 1, bar: 2 }, ['baz'])).toEqual({ picked: {}, omitted: { foo: 1, bar: 2 }, missingKeys: ['baz'] }); | ||
| }); | ||
| test('takeProperties using function', () => { | ||
| expect(takeProperties({ foo: 1, bar: 2 }, (_key, val) => val < 2)).toEqual({ filtered: { foo: 1 }, rejected: { bar: 2 }, undefined: {} }); | ||
| expect(takeProperties({ foo: 3, bar: 2 }, (_key, val) => val < 2)).toEqual({ filtered: {}, rejected: { foo: 3, bar: 2 }, undefined: {} }); | ||
| test('partitionProperties using function', () => { | ||
| expect(partitionProperties({ foo: 1, bar: 2 }, (_key, val) => val < 2)).toEqual({ picked: { foo: 1 }, omitted: { bar: 2 }, missingKeys: [] }); | ||
| expect(partitionProperties({ foo: 3, bar: 2 }, (_key, val) => val < 2)).toEqual({ picked: {}, omitted: { foo: 3, bar: 2 }, missingKeys: [] }); | ||
| }); | ||
@@ -88,3 +112,2 @@ test('isEmptyArray', () => { | ||
| expect(isPrimitive(new String('foo'))).toBeFalsy(); | ||
| expect(isPrimitive(Symbol('foo'))).toBeTruthy(); | ||
| expect(isPrimitive(false)).toBeTruthy(); | ||
@@ -95,38 +118,54 @@ expect(isPrimitive(new Boolean(false))).toBeFalsy(); | ||
| }); | ||
| test('isArrayOfPrimitives', () => { | ||
| expect(isArrayOfPrimitives([])).toBeFalsy(); | ||
| expect(isArrayOfPrimitives([{}])).toBeFalsy(); | ||
| expect(isArrayOfPrimitives([[]])).toBeFalsy(); | ||
| expect(isArrayOfPrimitives([false])).toBeTruthy(); | ||
| expect(isArrayOfPrimitives([false, 'foo', 1])).toBeTruthy(); | ||
| expect(isArrayOfPrimitives([false, new String('foo'), 1])).toBeFalsy(); | ||
| expect(isArrayOfPrimitives(1)).toBeFalsy(); | ||
| test('isPrimitiveWrapper', () => { | ||
| expect(isPrimitiveWrapper('foo')).toBeFalsy(); | ||
| expect(isPrimitiveWrapper(false)).toBeFalsy(); | ||
| expect(isPrimitiveWrapper(5)).toBeFalsy(); | ||
| expect(isPrimitiveWrapper(String('foo'))).toBeFalsy(); | ||
| expect(isPrimitiveWrapper(Boolean(false))).toBeFalsy(); | ||
| expect(isPrimitiveWrapper(Number(5))).toBeFalsy(); | ||
| expect(isPrimitiveWrapper(new String('foo'))).toBeTruthy(); | ||
| expect(isPrimitiveWrapper(new Boolean(false))).toBeTruthy(); | ||
| expect(isPrimitiveWrapper(new Number(5))).toBeTruthy(); | ||
| }); | ||
| test('isArrayOfType', () => { | ||
| expect(isArrayOfType([], 'string')).toBeFalsy(); | ||
| expect(isArrayOfType([{}], 'string')).toBeFalsy(); | ||
| expect(isArrayOfType(['foo', 'bar'], 'string')).toBeTruthy(); | ||
| expect(isArrayOfType(['foo', 'bar'], 'number')).toBeFalsy(); | ||
| expect(isArrayOfType([1, 2], 'number')).toBeTruthy(); | ||
| expect(isArrayOfType([1, 'bar'], 'number')).toBeFalsy(); | ||
| expect(isArrayOfType(['foo', 1], 'string')).toBeFalsy(); | ||
| test('unboxPrimitiveWrapper', () => { | ||
| expect(unboxPrimitiveWrapper([])).toEqual([]); | ||
| expect(unboxPrimitiveWrapper({})).toEqual({}); | ||
| expect(unboxPrimitiveWrapper('foo')).toBe('foo'); | ||
| expect(unboxPrimitiveWrapper(false)).toBe(false); | ||
| expect(unboxPrimitiveWrapper(5)).toBe(5); | ||
| expect(unboxPrimitiveWrapper(new String('foo'))).toBe('foo'); | ||
| expect(unboxPrimitiveWrapper(new Boolean(false))).toBe(false); | ||
| expect(unboxPrimitiveWrapper(new Number(5))).toBe(5); | ||
| }); | ||
| test('isArrayWhereEvery', () => { | ||
| expect(isArrayWhereEvery([[], []], isEmptyArray)).toBeTruthy(); | ||
| expect(isArrayWhereEvery([], isEmptyArray)).toBeFalsy(); | ||
| expect(isArrayWhereEvery([{ foo: 1 }, { bar: 2 }], isObjectLiteral)).toBeTruthy(); | ||
| expect(isArrayWhereEvery([{ foo: 1 }, new Date()], isObjectLiteral)).toBeFalsy(); | ||
| expect(isArrayWhereEvery([], isEmptyArray)).toBeTruthy(); | ||
| expect(isArrayWhereEvery([{ foo: 1 }, { bar: 2 }], isPlainObject)).toBeTruthy(); | ||
| expect(isArrayWhereEvery([{ foo: 1 }, new Date()], isPlainObject)).toBeFalsy(); | ||
| }); | ||
| test('isObjectLiteralWhereEvery', () => { | ||
| expect(isObjectLiteralWhereEvery([[], []], isEmptyArray)).toBeFalsy(); | ||
| expect(isObjectLiteralWhereEvery({ foo: [], bar: [] }, isEmptyArray)).toBeTruthy(); | ||
| expect(isObjectLiteralWhereEvery({}, isEmptyObjectLiteral)).toBeFalsy(); | ||
| expect(isObjectLiteralWhereEvery({ foo: { a: 1 }, bar: { b: 2 } }, isObjectLiteral)).toBeTruthy(); | ||
| expect(isPlainObjectWhereEvery([[], []], isEmptyArray)).toBeFalsy(); | ||
| expect(isPlainObjectWhereEvery({ foo: [], bar: [] }, isEmptyArray)).toBeTruthy(); | ||
| expect(isPlainObjectWhereEvery({}, isEmptyPlainObject)).toBeTruthy(); | ||
| expect(isPlainObjectWhereEvery({ foo: { a: 1 }, bar: { b: 2 } }, isPlainObject)).toBeTruthy(); | ||
| }); | ||
| test('isEmptyObjectLiteral', () => { | ||
| expect(isEmptyObjectLiteral({})).toBeTruthy(); | ||
| expect(isEmptyObjectLiteral([])).toBeFalsy(); | ||
| expect(isEmptyObjectLiteral({ foo: 1 })).toBeFalsy(); | ||
| expect(isEmptyObjectLiteral(new Date())).toBeFalsy(); | ||
| test('isPlainObject', () => { | ||
| expect(isPlainObject([])).toBeFalsy(); | ||
| expect(isPlainObject({})).toBeTruthy(); | ||
| expect(isPlainObject(Object.create(null))).toBeTruthy(); | ||
| expect(isPlainObject(1)).toBeFalsy(); | ||
| expect(isPlainObject('foo')).toBeFalsy(); | ||
| expect(isPlainObject(true)).toBeFalsy(); | ||
| expect(isPlainObject(false)).toBeFalsy(); | ||
| expect(isPlainObject(new Date())).toBeFalsy(); | ||
| expect(isPlainObject(new Number(1))).toBeFalsy(); | ||
| expect(isPlainObject(new String('foo'))).toBeFalsy(); | ||
| expect(isPlainObject(new Boolean(false))).toBeFalsy(); | ||
| }); | ||
| test('isEmptyPlainObject', () => { | ||
| expect(isEmptyPlainObject({})).toBeTruthy(); | ||
| expect(isEmptyPlainObject([])).toBeFalsy(); | ||
| expect(isEmptyPlainObject({ foo: 1 })).toBeFalsy(); | ||
| expect(isEmptyPlainObject(new Date())).toBeFalsy(); | ||
| }); | ||
| test('removeArrayElementByIndex', () => { | ||
@@ -154,5 +193,5 @@ expect(removeArrayElementByIndex([1, 2, 3], 1)).toEqual([1, 3]); | ||
| }); | ||
| test('sortProperties', () => { | ||
| expect(Object.keys(sortProperties({ b: 2, a: 1, c: 3 }))).not.toEqual(['b', 'a', 'c']); | ||
| expect(Object.keys(sortProperties({ b: 2, a: 1, c: 3 }))).toEqual(['a', 'b', 'c']); | ||
| test('toSortedObject', () => { | ||
| expect(Object.keys(toSortedObject({ b: 2, a: 1, c: 3 }))).not.toEqual(['b', 'a', 'c']); | ||
| expect(Object.keys(toSortedObject({ b: 2, a: 1, c: 3 }))).toEqual(['a', 'b', 'c']); | ||
| }); | ||
@@ -168,13 +207,13 @@ test('range', () => { | ||
| }); | ||
| test('duplicate', () => { | ||
| expect(duplicate(2, 3)).toEqual([2, 2, 2]); | ||
| expect(duplicate(['foo', 0], 3)).toEqual([['foo', 0], ['foo', 0], ['foo', 0]]); | ||
| expect(duplicate({ num: 1 }, 2, (value, i) => ({ ...value, id: i + 1 }))).toEqual([{ num: 1, id: 1 }, { num: 1, id: 2 }]); | ||
| test('repeat', () => { | ||
| expect(repeat(2, 3)).toEqual([2, 2, 2]); | ||
| expect(repeat(['foo', 0], 3)).toEqual([['foo', 0], ['foo', 0], ['foo', 0]]); | ||
| expect(repeat({ num: 1 }, 2, (value, i) => ({ ...value, id: i + 1 }))).toEqual([{ num: 1, id: 1 }, { num: 1, id: 2 }]); | ||
| }); | ||
| test('differenceArraysOfPrimitives', () => { | ||
| expect(differenceArraysOfPrimitives([2], [2])).toEqual([]); | ||
| expect(differenceArraysOfPrimitives([2], [3])).toEqual([2]); | ||
| expect(differenceArraysOfPrimitives([1, 2, 3, 9], [1, 3, 4])).toEqual([2, 9]); | ||
| expect(differenceArraysOfPrimitives(['foo'], ['bar', 'foo'])).toEqual([]); | ||
| expect(differenceArraysOfPrimitives(['bar', 'foo'], ['foo'])).toEqual(['bar']); | ||
| test('differencePrimitives', () => { | ||
| expect(differencePrimitives([2], [2])).toEqual([]); | ||
| expect(differencePrimitives([2], [3])).toEqual([2]); | ||
| expect(differencePrimitives([1, 2, 3, 9], [1, 3, 4])).toEqual([2, 9]); | ||
| expect(differencePrimitives(['foo'], ['bar', 'foo'])).toEqual([]); | ||
| expect(differencePrimitives(['bar', 'foo'], ['foo'])).toEqual(['bar']); | ||
| }); | ||
@@ -186,7 +225,7 @@ test('unique', () => { | ||
| }); | ||
| test('cloneShape', () => { | ||
| expect(cloneShape(null)).toEqual(null); | ||
| expect(cloneShape('foo')).toEqual('foo'); | ||
| test('deepClonePlain', () => { | ||
| expect(deepClonePlain(null)).toEqual(null); | ||
| expect(deepClonePlain('foo')).toEqual('foo'); | ||
| const o = [1, { b: null, c: { d: [2, 5, 1, 0, { c: 3, d: 4 }], e: "world" }, a: "foo" }]; | ||
| const clonedO = cloneShape(o); | ||
| const clonedO = deepClonePlain(o); | ||
| expect(o[1].b).toBe(null); | ||
@@ -193,0 +232,0 @@ expect(clonedO[1].b).toBe(null); |
+1
-1
@@ -178,3 +178,3 @@ Apache License | ||
| Copyright 2021 Mathieu Decaffmeyer | ||
| Copyright 2025 Mathieu Decaffmeyer | ||
@@ -181,0 +181,0 @@ Licensed under the Apache License, Version 2.0 (the "License"); |
+14
-10
| { | ||
| "name": "object-array-utils", | ||
| "version": "4.0.0", | ||
| "version": "5.0.0", | ||
| "description": "Utilities for working with arrays and objects", | ||
| "funding": "https://github.com/mathieuprog/object-array-utils?sponsor=1", | ||
| "source": "src/index.ts", | ||
| "main": "dist/index.js", | ||
| "module": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "type": "module", | ||
| "exports": { | ||
| ".": { | ||
| "import": "./dist/index.js", | ||
| "types": "./dist/index.d.ts" | ||
| } | ||
| }, | ||
| "types": "./dist/index.d.ts", | ||
| "files": [ | ||
@@ -14,2 +18,3 @@ "dist" | ||
| "scripts": { | ||
| "build": "tsc -p tsconfig.json", | ||
| "test": "vitest" | ||
@@ -20,7 +25,6 @@ }, | ||
| "devDependencies": { | ||
| "@tsconfig/node18": "^18.2.2", | ||
| "rollup": "^3.29.3", | ||
| "typescript": "^5.2.2", | ||
| "vite": "^4.4.9", | ||
| "vitest": "^0.34.5" | ||
| "@tsconfig/node22": "^22.0.2", | ||
| "typescript": "^5.8.3", | ||
| "vite": "^6.3.5", | ||
| "vitest": "^3.2.4" | ||
| }, | ||
@@ -27,0 +31,0 @@ "repository": { |
+63
-82
@@ -6,26 +6,14 @@ # `object-array-utils` | ||
| ```javascript | ||
| import { isObjectLiteral } from 'object-array-utils'; | ||
| import { isPlainObject } from 'object-array-utils'; | ||
| isObjectLiteral({ prop: 1 }) // true | ||
| isObjectLiteral(new Date()) // false | ||
| isObjectLiteral([1]) // false | ||
| isPlainObject({ prop: 1 }) // true | ||
| isPlainObject(new Date()) // false | ||
| isPlainObject([1]) // false | ||
| import { isEmptyObjectLiteral } from 'object-array-utils'; | ||
| import { isEmptyPlainObject } from 'object-array-utils'; | ||
| isEmptyObjectLiteral({}) // true | ||
| isEmptyObjectLiteral(new Date()) // false | ||
| isEmptyObjectLiteral([]) // false | ||
| isEmptyPlainObject({}) // true | ||
| isEmptyPlainObject(new Date()) // false | ||
| isEmptyPlainObject([]) // false | ||
| import { isObjectInstance } from 'object-array-utils'; | ||
| isObjectInstance({ prop: 1 }) // false | ||
| isObjectInstance(new Date()) // true | ||
| isObjectInstance([1]) // false | ||
| import { isObject } from 'object-array-utils'; | ||
| isObject({ prop: 1 }) // true | ||
| isObject(new Date()) // true | ||
| isObject([1]) // false | ||
| import { isArray } from 'object-array-utils'; | ||
@@ -39,15 +27,2 @@ | ||
| import { isArrayOfObjects } from 'object-array-utils'; | ||
| isArrayOfObjects([{ prop: 1 }, new Date()]) // true | ||
| isArrayOfObjects([1]) // false | ||
| isArrayOfObjects([]) // false | ||
| import { isArrayOfObjectLiterals } from 'object-array-utils'; | ||
| isArrayOfObjectLiterals([{ prop: 1 }, { prop: 2 }]) // true | ||
| isArrayOfObjectLiterals([{ prop: 1 }, new Date()]) // false | ||
| isArrayOfObjectLiterals([1]) // false | ||
| isArrayOfObjectLiterals([]) // false | ||
| import { isNullOrUndefined } from 'object-array-utils'; | ||
@@ -66,20 +41,20 @@ | ||
| import { filterProperties } from 'object-array-utils'; | ||
| import { pickProperties } from 'object-array-utils'; | ||
| filterProperties({ prop1: 1, prop2: 2 }, ['prop1', 'prop3']) // { prop1: 1 } | ||
| filterProperties<number>({ prop1: 1, prop2: 2 }, (_key, val) => val < 2) // { prop1: 1 } | ||
| pickProperties({ prop1: 1, prop2: 2 }, ['prop1', 'prop3']) // { prop1: 1 } | ||
| pickProperties<number>({ prop1: 1, prop2: 2 }, (_key, val) => val < 2) // { prop1: 1 } | ||
| import { rejectProperties } from 'object-array-utils'; | ||
| import { omitProperties } from 'object-array-utils'; | ||
| rejectProperties({ prop1: 1, prop2: 2 }, ['prop1', 'prop3']) // { prop2: 2 } | ||
| rejectProperties<number>({ prop1: 1, prop2: 2 }, (_key, val) => val < 2) // { prop2: 2 } | ||
| omitProperties({ prop1: 1, prop2: 2 }, ['prop1', 'prop3']) // { prop2: 2 } | ||
| omitProperties<number>({ prop1: 1, prop2: 2 }, (_key, val) => val < 2) // { prop2: 2 } | ||
| import { takeProperties } from 'object-array-utils'; | ||
| import { partitionProperties } from 'object-array-utils'; | ||
| takeProperties({ prop1: 1, prop2: 2 }, ['prop1', 'prop3']) // { filtered: { prop1: 1 }, rejected: { prop2: 2 } } | ||
| takeProperties<number>({ prop1: 1, prop2: 2 }, (_key, val) => val < 2) // { filtered: { prop1: 1 }, rejected: { prop2: 2 } } | ||
| partitionProperties({ prop1: 1, prop2: 2 }, ['prop1', 'prop3']) // { picked: { prop1: 1 }, omitted: { prop2: 2 }, missingKeys: ['prop3'] } | ||
| partitionProperties<number>({ prop1: 1, prop2: 2 }, (_key, val) => val < 2) // { picked: { prop1: 1 }, omitted: { prop2: 2 }, missingKeys: [] } | ||
| import { sortProperties } from 'object-array-utils'; | ||
| import { toSortedObject } from 'object-array-utils'; | ||
| sortProperties({ prop2: 2, prop1: 1 }) // { prop1: 1, prop2: 2 } | ||
| toSortedObject({ prop2: 2, prop1: 1 }) // { prop1: 1, prop2: 2 } | ||
@@ -100,7 +75,7 @@ import { removeArrayElement } from 'object-array-utils'; | ||
| import { isObjectSubset } from 'object-array-utils'; | ||
| import { isPlainObjectSubset } from 'object-array-utils'; | ||
| isObjectSubset({ prop1: 1, prop2: 2 }, { prop1: 1 }) // true | ||
| isObjectSubset({ prop1: { foo: 1, bar: 2 } }, prop2: 2 }, { prop1: { bar: 2 } }) // true | ||
| isObjectSubset({ prop1: [1, 2], prop2: 2 }, { prop1: [2] }) // true | ||
| isPlainObjectSubset({ prop1: 1, prop2: 2 }, { prop1: 1 }) // true | ||
| isPlainObjectSubset({ prop1: { foo: 1, bar: 2 } }, prop2: 2 }, { prop1: { bar: 2 } }) // true | ||
| isPlainObjectSubset({ prop1: [1, 2], prop2: 2 }, { prop1: [2] }) // true | ||
@@ -112,21 +87,28 @@ import { isArraySubset } from 'object-array-utils'; | ||
| import { areObjectsEqual } from 'object-array-utils'; | ||
| import { arePlainObjectsEqual, type AreNonPlainObjectsEqual } from 'object-array-utils'; | ||
| areObjectsEqual({ prop1: 1, prop2: 2 }, { prop2: 2, prop1: 1 }) // true | ||
| const areNonPlainObjectsEqual: AreNonPlainObjectsEqual = ((o1, o2) => { | ||
| if (o1 instanceof Date && o2 instanceof Date) return o1.getTime() === o2.getTime(); | ||
| throw new Error(); | ||
| }); | ||
| const opts = { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true }; | ||
| arePlainObjectsEqual({ prop1: 1, prop2: 2 }, { prop2: 2, prop1: 1 }, opts) // true | ||
| import { areArraysEqual } from 'object-array-utils'; | ||
| import { areArraysEqual, type AreNonPlainObjectsEqual } from 'object-array-utils'; | ||
| areArraysEqual([1, { prop1: 1, prop2: 2 }], [{ prop2: 2, prop1: 1 }, 1]) // true | ||
| const opts = { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true }; | ||
| areArraysEqual([1, { prop1: 1, prop2: 2 }], [{ prop2: 2, prop1: 1 }, 1], opts) // true | ||
| import { areValuesEqual } from 'object-array-utils'; | ||
| import { areDataEqual } from 'object-array-utils'; | ||
| areValuesEqual(new Date(), new Date()) // true | ||
| const opts = { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true }; | ||
| areDataEqual(new Date(), new Date(), opts) // true | ||
| import { cloneShape } from 'object-array-utils'; | ||
| import { deepClonePlain } from 'object-array-utils'; | ||
| cloneShape({ foo: [{ bar: 1 }] }) | ||
| deepClonePlain({ foo: [{ bar: 1 }] }) | ||
| import { deepFreeze } from 'object-array-utils'; | ||
| import { deepFreezePlain } from 'object-array-utils'; | ||
| deepFreeze({ foo: 1 }) | ||
| deepFreezePlain({ foo: 1 }) | ||
@@ -147,35 +129,33 @@ import { isPrimitive } from 'object-array-utils'; | ||
| import { isArrayOfPrimitives } from 'object-array-utils'; | ||
| import { isPrimitiveWrapper } from 'object-array-utils'; | ||
| isArrayOfPrimitives([1, 'foo']) // true | ||
| isArrayOfPrimitives([new Number(1), 'foo']) // false | ||
| isArrayOfPrimitives([]) // false | ||
| isPrimitiveWrapper(new Number(5)) // true | ||
| isPrimitiveWrapper(Number(5)) // false | ||
| isPrimitiveWrapper(5) // false | ||
| import { isArrayOfType } from 'object-array-utils'; | ||
| import { unboxPrimitiveWrapper } from 'object-array-utils'; | ||
| isArrayOfType(['foo', 'bar'], 'string') // true | ||
| isArrayOfType(['foo', 1], 'string') // false | ||
| isArrayOfType([1, 2], 'number') // true | ||
| isArrayOfType([], 'string') // false | ||
| unboxPrimitiveWrapper(new Number(5)) // 5 | ||
| unboxPrimitiveWrapper(5) // 5 | ||
| import { isArrayWhereEvery, isObjectLiteral } from 'object-array-utils'; | ||
| import { isArrayWhereEvery, isPlainObject } from 'object-array-utils'; | ||
| isArrayWhereEvery([{ foo: 1 }, { bar: 2 }], isObjectLiteral) // true | ||
| isArrayWhereEvery([{ foo: 1 }, new Date()], isObjectLiteral) // false | ||
| isArrayWhereEvery([], isObjectLiteral) // false | ||
| isArrayWhereEvery([{ foo: 1 }, { bar: 2 }], isPlainObject) // true | ||
| isArrayWhereEvery([{ foo: 1 }, new Date()], isPlainObject) // false | ||
| isArrayWhereEvery([], isPlainObject) // false | ||
| import { isObjectLiteralWhereEvery, isArray } from 'object-array-utils'; | ||
| import { isPlainObjectWhereEvery, isArray } from 'object-array-utils'; | ||
| isObjectLiteralWhereEvery({ foo: [1], bar: [2, 3] }, isArray) // true | ||
| isObjectLiteralWhereEvery({}, isArray) // false | ||
| isPlainObjectWhereEvery({ foo: [1], bar: [2, 3] }, isArray) // true | ||
| isPlainObjectWhereEvery({}, isArray) // false | ||
| import { differenceArraysOfPrimitives } from 'object-array-utils'; | ||
| import { differencePrimitives } from 'object-array-utils'; | ||
| differenceArraysOfPrimitives([1, 2, 3, 9], [1, 3, 4]) // [2, 9] | ||
| differencePrimitives([1, 2, 3, 9], [1, 3, 4]) // [2, 9] | ||
| import { duplicate } from 'object-array-utils'; | ||
| import { repeat } from 'object-array-utils'; | ||
| duplicate(1, 3) // [1, 1, 1] | ||
| duplicate(1, 3, (value, i) => value + 1) // [1, 2, 3] | ||
| duplicate({ name: 'John' }, 2, (v, i) => ({ id: i + 1, ...v })) // [{ id: 1, name: 'John' }, { id: 2, name: 'John' }] | ||
| repeat(1, 3) // [1, 1, 1] | ||
| repeat(1, 3, (value, i) => value + 1) // [1, 2, 3] | ||
| repeat({ name: 'John' }, 2, (v, i) => ({ id: i + 1, ...v })) // [{ id: 1, name: 'John' }, { id: 2, name: 'John' }] | ||
@@ -206,4 +186,5 @@ import { range } from 'object-array-utils'; | ||
| ```javascript | ||
| isArraySubset([{ foo: 1 }, { bar: 2 }], [{}, { bar: 2 }]) // true | ||
| isArraySubset([{ foo: 1 }, { bar: 2 }], [{}, { foo: 1 }]) // false | ||
| const opts = { areNonPlainObjectsEqual, unboxPrimitives: true, unorderedArrays: true }; | ||
| isArraySubset([{ foo: 1 }, { bar: 2 }], [{}, { bar: 2 }], opts) // true | ||
| isArraySubset([{ foo: 1 }, { bar: 2 }], [{}, { foo: 1 }], opts) // false | ||
| ``` | ||
@@ -210,0 +191,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
52590
2.95%4
-20%749
2.46%Yes
NaN192
-9%1
Infinity%