From dca64b5c440701d0202e06d3fd91f4c4acb531b7 Mon Sep 17 00:00:00 2001 From: Dongho Kim Date: Mon, 4 Nov 2024 22:08:13 +0900 Subject: [PATCH 1/3] fix(differenceBy): Update `differenceBy` function to handle array-like objects and filter out invalid values --- src/compat/array/differenceBy.spec.ts | 97 +++++++++++++++++++++++++++ src/compat/array/differenceBy.ts | 15 ++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/compat/array/differenceBy.spec.ts b/src/compat/array/differenceBy.spec.ts index 908ea8c39..551940d57 100644 --- a/src/compat/array/differenceBy.spec.ts +++ b/src/compat/array/differenceBy.spec.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from 'vitest'; import { differenceBy } from './differenceBy'; +import { range } from '../../math'; +import { args } from '../_internal/args'; +import { LARGE_ARRAY_SIZE } from '../_internal/LARGE_ARRAY_SIZE'; import { slice } from '../_internal/slice'; describe('differenceBy', () => { @@ -25,4 +28,98 @@ describe('differenceBy', () => { const actual = differenceBy([2, 1, 2, 3], [3, 4], [3, 2]); expect(actual).toEqual([1]); }); + + /** + * @see https://github.com/lodash/lodash/blob/6a2cc1dfcf7634fea70d1bc5bd22db453df67b42/test/difference-methods.spec.js#L1 + */ + + it(`should return the difference of two arrays`, () => { + const actual = differenceBy([2, 1], [2, 3]); + expect(actual).toEqual([1]); + }); + + it(`should return the difference of multiple arrays`, () => { + const actual = differenceBy([2, 1, 2, 3], [3, 4], [3, 2]); + expect(actual).toEqual([1]); + }); + + it(`should treat \`-0\` as \`0\``, () => { + const array = [-0, 0]; + + const actual = array.map(value => differenceBy(array, [value])); + + expect(actual).toEqual([[], []]); + + expect(differenceBy([-0, 1], [1])).toEqual([-0]); + }); + + it(`should match \`NaN\``, () => { + expect(differenceBy([1, NaN, 3], [NaN, 5, NaN])).toEqual([1, 3]); + }); + + it(`should work with large arrays`, () => { + const array1: unknown[] = range(LARGE_ARRAY_SIZE + 1); + const array2: unknown[] = range(LARGE_ARRAY_SIZE); + + const a = {}; + const b = {}; + const c = {}; + + array1.push(a, b, c); + array2.push(b, c, a); + + expect(differenceBy(array1, array2)).toEqual([LARGE_ARRAY_SIZE]); + }); + + it(`should work with large arrays of \`-0\` as \`0\``, () => { + const array = [-0, 0]; + + const actual = array.map(value => { + const largeArray = Array.from({ length: LARGE_ARRAY_SIZE }).map(() => value); + + return differenceBy(array, largeArray); + }); + + expect(actual).toEqual([[], []]); + + const largeArray = Array.from({ length: LARGE_ARRAY_SIZE }).map(() => 1); + expect(differenceBy([-0, 1], largeArray)).toEqual([-0]); + }); + + it(`should work with large arrays of \`NaN\``, () => { + const largeArray = Array.from({ length: LARGE_ARRAY_SIZE }).map(() => NaN); + expect(differenceBy([1, NaN, 3], largeArray)).toEqual([1, 3]); + }); + + it(`should work with large arrays of objects`, () => { + const object1 = {}; + const object2 = {}; + const largeArray = Array.from({ length: LARGE_ARRAY_SIZE }).map(() => ({})); + + expect(differenceBy([object1, object2], largeArray)).toEqual([object1, object2]); + }); + + it(`should work with \`arguments\` objects`, () => { + const array = [0, 1, null, 3]; + + expect(differenceBy(array, args)).toEqual([0, null]); + expect(differenceBy(args, array)).toEqual([2]); + }); + + it('should work with arrayLike objects', () => { + const array = { 0: 1, 1: 2, length: 2 }; + + expect(differenceBy(array, [2, 3])).toEqual([1]); + expect(differenceBy([1, 2, 3], array)).toEqual([3]); + expect(differenceBy([1, 2, 3], array, value => value)).toEqual([3]); + }); + + it('should return an empty array when the first array is not array-like object', () => { + expect(differenceBy('23', ['2', '3'])).toEqual([]); + }); + + it('should filter out values that are not arrays or array-like objects', () => { + expect(differenceBy(['2', '3'], '2', ['3'])).toEqual(['2']); + expect(differenceBy(['2', '3'], '2', ['3'], value => value)).toEqual(['2']); + }); }); diff --git a/src/compat/array/differenceBy.ts b/src/compat/array/differenceBy.ts index 556adfe3d..ae7765777 100644 --- a/src/compat/array/differenceBy.ts +++ b/src/compat/array/differenceBy.ts @@ -129,8 +129,19 @@ export function differenceBy(arr: ArrayLike | null | undefined, ...values: const iteratee = last(values); if (isArrayLikeObject(iteratee)) { - return differenceToolkit(Array.from(arr), flatten(values)); + return differenceToolkit(Array.from(arr), flatten(toValidArrays(values)) as T[]); } - return differenceByToolkit(Array.from(arr), flatten(values.slice(0, -1)), createIteratee(iteratee)); + return differenceByToolkit( + Array.from(arr), + flatten(toValidArrays(values.slice(0, -1))) as T[], + createIteratee(iteratee) + ); +} + +function toValidArrays(values: Array>) { + const filteredValues = values.filter(value => isArrayLikeObject(value)); + const arrays = filteredValues.map(value => Array.from(value)); + + return arrays; } From eefa6dfb8913420619411d10dc706788ccc8c479 Mon Sep 17 00:00:00 2001 From: Sojin Park Date: Sat, 9 Nov 2024 23:17:18 +0900 Subject: [PATCH 2/3] fix --- src/compat/_internal/flattenArrayLike.spec.ts | 39 +++++++++++++++++++ src/compat/_internal/flattenArrayLike.ts | 19 +++++++++ src/compat/array/differenceBy.ts | 21 +++------- 3 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 src/compat/_internal/flattenArrayLike.spec.ts create mode 100644 src/compat/_internal/flattenArrayLike.ts diff --git a/src/compat/_internal/flattenArrayLike.spec.ts b/src/compat/_internal/flattenArrayLike.spec.ts new file mode 100644 index 000000000..c3b33e51d --- /dev/null +++ b/src/compat/_internal/flattenArrayLike.spec.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest'; +import { flattenArrayLike } from './flattenArrayLike'; + +describe('flattenArrayLike', () => { + it('should flatten an array of array-like objects', () => { + const input: Array> = [ + { length: 3, 0: 'a', 1: 'b', 2: 'c' }, + { length: 2, 0: 'd', 1: 'e' }, + { length: 0 }, + ]; + const expectedOutput = ['a', 'b', 'c', 'd', 'e']; + expect(flattenArrayLike(input)).toEqual(expectedOutput); + }); + + it('should ignore non-array-like objects', () => { + const input: any[] = [{ length: 2, 0: 'x', 1: 'y' }, 3, { length: 1, 0: 'z' }]; + const expectedOutput = ['x', 'y', 'z']; + expect(flattenArrayLike(input)).toEqual(expectedOutput); + }); + + it('should return an empty array when input is empty', () => { + const input: Array> = []; + const expectedOutput: any[] = []; + expect(flattenArrayLike(input)).toEqual(expectedOutput); + }); + + it('should handle nested array-like objects', () => { + const input: Array> = [ + { length: 2, 0: [1, 2], 1: [3, 4] }, + { length: 1, 0: [5, 6] }, + ]; + const expectedOutput = [ + [1, 2], + [3, 4], + [5, 6], + ]; + expect(flattenArrayLike(input)).toEqual(expectedOutput); + }); +}); diff --git a/src/compat/_internal/flattenArrayLike.ts b/src/compat/_internal/flattenArrayLike.ts new file mode 100644 index 000000000..b4ad66b94 --- /dev/null +++ b/src/compat/_internal/flattenArrayLike.ts @@ -0,0 +1,19 @@ +import { isArrayLikeObject } from '../predicate/isArrayLikeObject.ts'; + +export function flattenArrayLike(values: Array>): T[] { + const result: T[] = []; + + for (let i = 0; i < values.length; i++) { + const arrayLike = values[i]; + + if (!isArrayLikeObject(arrayLike)) { + continue; + } + + for (let j = 0; j < arrayLike.length; j++) { + result.push(arrayLike[j]); + } + } + + return result; +} diff --git a/src/compat/array/differenceBy.ts b/src/compat/array/differenceBy.ts index ae7765777..4e446c1b3 100644 --- a/src/compat/array/differenceBy.ts +++ b/src/compat/array/differenceBy.ts @@ -2,6 +2,7 @@ import { flatten } from './flatten.ts'; import { last } from './last.ts'; import { difference as differenceToolkit } from '../../array/difference.ts'; import { differenceBy as differenceByToolkit } from '../../array/differenceBy.ts'; +import { flattenArrayLike } from '../_internal/flattenArrayLike.ts'; import { isArrayLikeObject } from '../predicate/isArrayLikeObject.ts'; import { iteratee as createIteratee } from '../util/iteratee.ts'; @@ -121,27 +122,17 @@ export function differenceBy(array: ArrayLike | null | undefined, ...value * @param {...any[]} values - Multiple arrays containing elements to be excluded from the primary array. * @returns {T[]} A new array containing the elements that are present in the primary array but not in the values arrays. */ -export function differenceBy(arr: ArrayLike | null | undefined, ...values: any[]): T[] { +export function differenceBy(arr: ArrayLike | null | undefined, ..._values: any[]): T[] { if (!isArrayLikeObject(arr)) { return []; } - const iteratee = last(values); + const iteratee = last(_values); + const values = flattenArrayLike(_values); if (isArrayLikeObject(iteratee)) { - return differenceToolkit(Array.from(arr), flatten(toValidArrays(values)) as T[]); + return differenceToolkit(Array.from(arr), values); } - return differenceByToolkit( - Array.from(arr), - flatten(toValidArrays(values.slice(0, -1))) as T[], - createIteratee(iteratee) - ); -} - -function toValidArrays(values: Array>) { - const filteredValues = values.filter(value => isArrayLikeObject(value)); - const arrays = filteredValues.map(value => Array.from(value)); - - return arrays; + return differenceByToolkit(Array.from(arr), values, createIteratee(iteratee)); } From d4f52baff7cd0070f9700d59985caab68ee70c26 Mon Sep 17 00:00:00 2001 From: Sojin Park Date: Sat, 9 Nov 2024 23:19:07 +0900 Subject: [PATCH 3/3] fix --- src/compat/array/differenceBy.spec.ts | 1 + src/compat/array/differenceBy.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compat/array/differenceBy.spec.ts b/src/compat/array/differenceBy.spec.ts index 551940d57..d5b6e638b 100644 --- a/src/compat/array/differenceBy.spec.ts +++ b/src/compat/array/differenceBy.spec.ts @@ -18,6 +18,7 @@ describe('differenceBy', () => { let args: any; differenceBy([2.1, 1.2], [2.3, 3.4], function () { + // eslint-disable-next-line args || (args = slice.call(arguments)); }); diff --git a/src/compat/array/differenceBy.ts b/src/compat/array/differenceBy.ts index 4e446c1b3..7aab8d336 100644 --- a/src/compat/array/differenceBy.ts +++ b/src/compat/array/differenceBy.ts @@ -1,4 +1,3 @@ -import { flatten } from './flatten.ts'; import { last } from './last.ts'; import { difference as differenceToolkit } from '../../array/difference.ts'; import { differenceBy as differenceByToolkit } from '../../array/differenceBy.ts';