diff --git a/src/array/tests/unique.test.ts b/src/array/tests/unique.test.ts index b2cc44a5..106f9665 100644 --- a/src/array/tests/unique.test.ts +++ b/src/array/tests/unique.test.ts @@ -23,4 +23,24 @@ describe('unique function', () => { expect(c.id).toBe('c') expect(c.word).toBe('yolo') }) + test('correctly handles non string, number or symbol values', () => { + const list: any[] = [ + null, + null, + true, + true, + 'true', + false, + { id: 'a', word: 'hello' }, + { id: 'a', word: 'hello' } + ] + const result = _.unique(list, val => (val && val.id) ?? val) + expect(result).toEqual([ + null, + true, + 'true', + false, + { id: 'a', word: 'hello' } + ]) + }) }) diff --git a/src/array/unique.ts b/src/array/unique.ts index f1d1e4dd..0536f84a 100644 --- a/src/array/unique.ts +++ b/src/array/unique.ts @@ -3,15 +3,20 @@ * Accepts an optional identity function to convert each item in the * list to a comparable identity value */ -export const unique = ( +export const unique = ( array: readonly T[], toKey?: (item: T) => K ): T[] => { - const valueMap = array.reduce((acc, item) => { - const key = toKey ? toKey(item) : (item as any as string | number | symbol) - if (acc[key]) return acc - acc[key] = item - return acc - }, {} as Record) - return Object.values(valueMap) + if (toKey) { + const keys = new Set() + return array.reduce((acc, item) => { + const key = toKey(item) + if (!keys.has(key)) { + keys.add(key) + acc.push(item) + } + return acc + }, [] as T[]) + } + return [...new Set(array)] }