object-array-utils
Advanced tools
+2
-1
@@ -13,2 +13,3 @@ export type PlainObject<T = unknown> = Record<string, T>; | ||
| declare function isPlainObjectWhereEvery<T>(o: unknown, predicate: (v: unknown) => v is T): o is PlainObject<T>; | ||
| declare function hasArrayDuplicates<T, K = T>(array: readonly T[], keyFn?: (item: T) => K): boolean; | ||
| export declare enum ComparisonResult { | ||
@@ -79,2 +80,2 @@ Equal = "EQUAL", | ||
| declare function makeCopyOnWriteObjectSetter<T extends PlainObject>(base: T): <K extends keyof T>(key: K, value: T[K]) => T; | ||
| export { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, hasProperty, hasProperties, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isNullish, isPlainObject, isPlainObjectWhereEvery, isPlainObjectSubset, isPrimitive, isPrimitiveWrapper, range, omitProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unboxPrimitiveWrapper, partitionProperties, unique, makeCopyOnWriteObjectSetter, }; | ||
| export { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, hasProperty, hasProperties, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isNullish, isPlainObject, isPlainObjectWhereEvery, isPlainObjectSubset, isPrimitive, isPrimitiveWrapper, range, omitProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unboxPrimitiveWrapper, partitionProperties, unique, makeCopyOnWriteObjectSetter, hasArrayDuplicates, }; |
+16
-1
@@ -31,2 +31,17 @@ function isPrimitive(v) { | ||
| } | ||
| function hasArrayDuplicates(array, keyFn = (x => x)) { | ||
| if (!Array.isArray(array)) { | ||
| throw new Error('expected array'); | ||
| } | ||
| if (array.length < 2) | ||
| return false; | ||
| const seen = new Set(); | ||
| for (const item of array) { | ||
| const key = keyFn(item); | ||
| if (seen.has(key)) | ||
| return true; | ||
| seen.add(key); | ||
| } | ||
| return false; | ||
| } | ||
| export var ComparisonResult; | ||
@@ -447,2 +462,2 @@ (function (ComparisonResult) { | ||
| } | ||
| export { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, hasProperty, hasProperties, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isNullish, isPlainObject, isPlainObjectWhereEvery, isPlainObjectSubset, isPrimitive, isPrimitiveWrapper, range, omitProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unboxPrimitiveWrapper, partitionProperties, unique, makeCopyOnWriteObjectSetter, }; | ||
| export { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, hasProperty, hasProperties, isArraySubset, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isNullish, isPlainObject, isPlainObjectWhereEvery, isPlainObjectSubset, isPrimitive, isPrimitiveWrapper, range, omitProperties, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unboxPrimitiveWrapper, partitionProperties, unique, makeCopyOnWriteObjectSetter, hasArrayDuplicates, }; |
+58
-1
| import { expect, test } from 'vitest'; | ||
| 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, makeCopyOnWriteObjectSetter } from './index'; | ||
| import { areArraysEqual, arePlainObjectsEqual, areDataEqual, deepClonePlain, deepFreezePlain, differencePrimitives, repeat, pickProperties, omitProperties, partitionProperties, hasProperties, hasArrayDuplicates, isArrayWhereEvery, isEmptyArray, isEmptyPlainObject, isPlainObject, isPlainObjectWhereEvery, isPrimitive, isPrimitiveWrapper, unboxPrimitiveWrapper, range, removeArrayElement, removeArrayElementByIndex, removeArrayElements, toSortedObject, unique, makeCopyOnWriteObjectSetter } from './index'; | ||
| test('areDataEqual', () => { | ||
@@ -257,1 +257,58 @@ const areNonPlainObjectsEqual = () => { throw new Error(); }; | ||
| }); | ||
| test('hasArrayDuplicates', () => { | ||
| // Basic cases with duplicates | ||
| expect(hasArrayDuplicates([1, 2, 3, 2])).toBeTruthy(); | ||
| expect(hasArrayDuplicates([1, 1, 2, 3])).toBeTruthy(); | ||
| expect(hasArrayDuplicates(['a', 'b', 'a'])).toBeTruthy(); | ||
| expect(hasArrayDuplicates([true, false, true])).toBeTruthy(); | ||
| // Basic cases without duplicates | ||
| expect(hasArrayDuplicates([1, 2, 3, 4])).toBeFalsy(); | ||
| expect(hasArrayDuplicates(['a', 'b', 'c'])).toBeFalsy(); | ||
| expect(hasArrayDuplicates([true, false])).toBeFalsy(); | ||
| // Edge cases | ||
| expect(hasArrayDuplicates([])).toBeFalsy(); | ||
| expect(hasArrayDuplicates([1])).toBeFalsy(); | ||
| expect(hasArrayDuplicates([null, undefined])).toBeFalsy(); | ||
| expect(hasArrayDuplicates([null, null])).toBeTruthy(); | ||
| expect(hasArrayDuplicates([undefined, undefined])).toBeTruthy(); | ||
| // Mixed types (no duplicates due to type differences) | ||
| expect(hasArrayDuplicates([1, '1', true])).toBeFalsy(); | ||
| expect(hasArrayDuplicates([0, false, ''])).toBeFalsy(); | ||
| // Objects (always unique unless same reference) | ||
| const obj1 = { x: 1 }; | ||
| const obj2 = { x: 1 }; | ||
| expect(hasArrayDuplicates([obj1, obj2])).toBeFalsy(); // different objects | ||
| expect(hasArrayDuplicates([obj1, obj1])).toBeTruthy(); // same reference | ||
| // Error cases | ||
| expect(() => hasArrayDuplicates(null)).toThrow('expected array'); | ||
| expect(() => hasArrayDuplicates(undefined)).toThrow('expected array'); | ||
| expect(() => hasArrayDuplicates({})).toThrow('expected array'); | ||
| expect(() => hasArrayDuplicates('string')).toThrow('expected array'); | ||
| }); | ||
| test('hasArrayDuplicates with keyFn', () => { | ||
| // Using keyFn to check duplicates by object property | ||
| const users = [ | ||
| { id: 1, name: 'Alice' }, | ||
| { id: 2, name: 'Bob' }, | ||
| { id: 1, name: 'Alice Clone' } | ||
| ]; | ||
| expect(hasArrayDuplicates(users, user => user.id)).toBeTruthy(); // duplicate id | ||
| expect(hasArrayDuplicates(users, user => user.name)).toBeFalsy(); // all names unique | ||
| // Using keyFn for case-insensitive string comparison | ||
| expect(hasArrayDuplicates(['Hello', 'world', 'HELLO'], s => s.toLowerCase())).toBeTruthy(); | ||
| expect(hasArrayDuplicates(['Hello', 'World'], s => s.toLowerCase())).toBeFalsy(); | ||
| // Using keyFn to extract specific values | ||
| const items = [ | ||
| { type: 'A', value: 10 }, | ||
| { type: 'B', value: 20 }, | ||
| { type: 'A', value: 30 } | ||
| ]; | ||
| expect(hasArrayDuplicates(items, item => item.type)).toBeTruthy(); // duplicate type 'A' | ||
| expect(hasArrayDuplicates(items, item => item.value)).toBeFalsy(); // all values unique | ||
| // Using keyFn with numbers | ||
| expect(hasArrayDuplicates([1.1, 2.2, 1.9], n => Math.floor(n))).toBeTruthy(); // floors to 1, 2, 1 | ||
| expect(hasArrayDuplicates([1.1, 2.2, 3.3], n => Math.floor(n))).toBeFalsy(); // floors to 1, 2, 3 | ||
| // Edge cases with keyFn | ||
| expect(hasArrayDuplicates([], user => user)).toBeFalsy(); | ||
| expect(hasArrayDuplicates([{ x: 1 }], item => item.x)).toBeFalsy(); | ||
| }); |
+1
-1
| { | ||
| "name": "object-array-utils", | ||
| "version": "6.0.0", | ||
| "version": "6.1.0", | ||
| "description": "Utilities for working with arrays and objects", | ||
@@ -5,0 +5,0 @@ "funding": "https://github.com/mathieuprog/object-array-utils?sponsor=1", |
+9
-1
@@ -189,4 +189,12 @@ # `object-array-utils` | ||
| set('b', 99) === draft; // true | ||
| ``` | ||
| import { hasArrayDuplicates } from 'object-array-utils'; | ||
| hasArrayDuplicates([1, 2, 3]) // false | ||
| hasArrayDuplicates([1, 2, 1]) // true | ||
| hasArrayDuplicates( | ||
| [{ name: 'John', age: 27 }, { name: 'James', age: 42 }, { name: 'Joe', age: 27 }], | ||
| ({ age }) => age | ||
| ) // true | ||
| ## Limitations | ||
@@ -193,0 +201,0 @@ |
58076
7.02%859
9.57%216
3.85%