feat(flatten): add compatibility with lodash (#330)

* feat(flatten): add compatibility

* Add bench

* Add doc

* fix jsdoc
This commit is contained in:
Dayong Lee 2024-07-31 21:54:21 +09:00 committed by GitHub
parent 25217a571e
commit 2d1c406398
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 110 additions and 3 deletions

View File

@ -1,5 +1,6 @@
import { bench, describe } from 'vitest';
import { flatten as flattenToolkit } from 'es-toolkit';
import { flatten as flattenCompatToolkit } from 'es-toolkit/compat';
import { flattenDepth as flattenDepthLodash } from 'lodash';
const createNestedArray = (values: any[]) => {
@ -17,6 +18,10 @@ describe('flatten', () => {
flattenToolkit(arr, 30);
});
bench('es-toolkit/flatten (compat)', () => {
flattenCompatToolkit(arr, 30);
});
bench('lodash/flattenDepth', () => {
flattenDepthLodash(arr, 30);
});

View File

@ -64,7 +64,7 @@ Even if a feature is marked "in review," it might already be under review to ens
| [fill](https://lodash.com/docs/4.17.15#fill) | ✅ |
| [findIndex](https://lodash.com/docs/4.17.15#findIndex) | ❌ |
| [findLastIndex](https://lodash.com/docs/4.17.15#findIndex) | ❌ |
| [flatten](https://lodash.com/docs/4.17.15#flatten) | 📝 |
| [flatten](https://lodash.com/docs/4.17.15#flatten) | |
| [flattenDeep](https://lodash.com/docs/4.17.15#flattenDeep) | 📝 |
| [flattenDepth](https://lodash.com/docs/4.17.15#flattenDepth) | 📝 |
| [fromPairs](https://lodash.com/docs/4.17.15#fromPairs) | ❌ |

View File

@ -65,7 +65,7 @@ chunk([1, 2, 3, 4], 0);
| [fill](https://lodash.com/docs/4.17.15#fill) | ✅ |
| [findIndex](https://lodash.com/docs/4.17.15#findIndex) | ❌ |
| [findLastIndex](https://lodash.com/docs/4.17.15#findIndex) | ❌ |
| [flatten](https://lodash.com/docs/4.17.15#flatten) | 📝 |
| [flatten](https://lodash.com/docs/4.17.15#flatten) | |
| [flattenDeep](https://lodash.com/docs/4.17.15#flattenDeep) | 📝 |
| [flattenDepth](https://lodash.com/docs/4.17.15#flattenDepth) | 📝 |
| [fromPairs](https://lodash.com/docs/4.17.15#fromPairs) | ❌ |

View File

@ -64,7 +64,7 @@ chunk([1, 2, 3, 4], 0);
| [fill](https://lodash.com/docs/4.17.15#fill) | ✅ |
| [findIndex](https://lodash.com/docs/4.17.15#findIndex) | ❌ |
| [findLastIndex](https://lodash.com/docs/4.17.15#findIndex) | ❌ |
| [flatten](https://lodash.com/docs/4.17.15#flatten) | 📝 |
| [flatten](https://lodash.com/docs/4.17.15#flatten) | |
| [flattenDeep](https://lodash.com/docs/4.17.15#flattenDeep) | 📝 |
| [flattenDepth](https://lodash.com/docs/4.17.15#flattenDepth) | 📝 |
| [fromPairs](https://lodash.com/docs/4.17.15#fromPairs) | ❌ |

View File

@ -0,0 +1,54 @@
import { describe, it, expect } from 'vitest';
import { flatten } from './flatten';
import { args } from '../_internal/args';
describe('flatten', () => {
it('should flatten `arguments` objects', () => {
const array = [args, [args]];
const expected = [1, 2, 3, args];
const actual = flatten(array);
expect(actual).toEqual(expected);
});
it('should treat sparse arrays as dense', () => {
const array = [[1, 2, 3], Array(3)];
const expected = [1, 2, 3, undefined, undefined, undefined];
const actual = flatten(array);
expect(actual).toEqual(expected);
expect('4' in actual).toBeTruthy();
});
it('should flatten objects with a truthy `Symbol.isConcatSpreadable` value', () => {
const object = { 0: 'a', length: 1, [Symbol.isConcatSpreadable]: true };
const array = [object];
const expected = ['a'];
const actual = flatten(array);
expect(actual).toEqual(expected);
});
it('should work with empty arrays', () => {
const array = [[], [[]], [[], [[[]]]]];
const expected = [[], [], [[[]]]];
const actual = flatten(array);
expect(actual).toEqual(expected);
});
it('should support flattening of nested arrays', () => {
const array = [1, [2, [3, [4]], 5]];
const expected = [1, 2, [3, [4]], 5];
const actual = flatten(array);
expect(actual).toEqual(expected);
});
it('should return an empty array for non array-like objects', () => {
const nonArray = { 0: 'a' };
const expected: [] = [];
const actual = flatten(nonArray);
expect(actual).toEqual(expected);
});
});

View File

@ -0,0 +1,47 @@
/**
* Flattens an array up to the specified depth.
*
* @template T - The type of elements within the array.
* @template D - The depth to which the array should be flattened.
* @param {T[] | object} value - The object to flatten.
* @param {D} depth - The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
* @returns {Array<FlatArray<T[], D>> | []} A new array that has been flattened.
*
* @example
* const arr = flatten([1, [2, 3], [4, [5, 6]]], 1);
* // Returns: [1, 2, 3, 4, [5, 6]]
*
* const arr = flatten([1, [2, 3], [4, [5, 6]]], 2);
* // Returns: [1, 2, 3, 4, 5, 6]
*/
export function flatten<T, D extends number = 1>(value: T[] | object, depth = 1 as D): Array<FlatArray<T[], D>> | [] {
const result: Array<FlatArray<T[], D>> = [];
const flooredDepth = Math.floor(depth);
if (!Array.isArray(value)) {
return result;
}
const recursive = (arr: T[], currentDepth: number) => {
for (const item of arr) {
if (
currentDepth < flooredDepth &&
(Array.isArray(item) ||
Boolean(item?.[Symbol.isConcatSpreadable as keyof object]) ||
(item !== null && typeof item === 'object' && Object.prototype.toString.call(item) === '[object Arguments]'))
) {
if (Array.isArray(item)) {
recursive(item, currentDepth + 1);
} else {
recursive(Array.from(item as T[]), currentDepth + 1);
}
} else {
result.push(item as FlatArray<T[], D>);
}
}
};
recursive(value, 0);
return result;
}

View File

@ -28,6 +28,7 @@ export { chunk } from './array/chunk.ts';
export { concat } from './array/concat.ts';
export { difference } from './array/difference.ts';
export { fill } from './array/fill.ts';
export { flatten } from './array/flatten.ts';
export { zipObjectDeep } from './array/zipObjectDeep.ts';
export { head as first } from '../index.ts';