diff --git a/src/utils/__tests__/group-async.spec-d.ts b/src/utils/__tests__/group-async.spec-d.ts new file mode 100644 index 00000000..c9eed0e0 --- /dev/null +++ b/src/utils/__tests__/group-async.spec-d.ts @@ -0,0 +1,19 @@ +/** + * @file Type Tests - groupAsync + * @module tutils/utils/tests/unit-d/groupAsync + */ + +import type Vehicle from '#fixtures/types/vehicle' +import type testSubject from '../group-async' + +describe('unit-d:utils/groupAsync', () => { + it('should return Promise>', () => { + // Arrange + type T = Vehicle[] + type K = Vehicle['year'] + type Expect = Promise> + + // Expect + expectTypeOf>().returns.toEqualTypeOf() + }) +}) diff --git a/src/utils/__tests__/group-async.spec.ts b/src/utils/__tests__/group-async.spec.ts new file mode 100644 index 00000000..11e13b4e --- /dev/null +++ b/src/utils/__tests__/group-async.spec.ts @@ -0,0 +1,15 @@ +/** + * @file Unit Tests - groupAsync + * @module tutils/utils/tests/unit/groupAsync + */ + +import testSubject from '../group-async' + +describe('unit:utils/groupAsync', () => { + it('should return groups object', async () => { + expect(await testSubject([3.1, 4.2, 3.3], Math.floor)).to.eql({ + 3: [3.1, 3.3], + 4: [4.2] + }) + }) +}) diff --git a/src/utils/__tests__/group.spec.ts b/src/utils/__tests__/group.spec.ts index 318432f9..b6780e9d 100644 --- a/src/utils/__tests__/group.spec.ts +++ b/src/utils/__tests__/group.spec.ts @@ -7,15 +7,9 @@ import testSubject from '../group' describe('unit:utils/group', () => { it('should return groups object', () => { - // Arrange - const cases: [...Parameters>, object][] = [ - [[], vi.fn(), {}], - [[3.1, 4.2, 3.3], Math.floor, { 3: [3.1, 3.3], 4: [4.2] }] - ] - - // Act + Expect - cases.forEach(([arr, key, expected]) => { - expect(testSubject(arr, key)).to.eql(expected) + expect(testSubject([3.1, 4.2, 3.3], Math.floor)).to.eql({ + 3: [3.1, 3.3], + 4: [4.2] }) }) }) diff --git a/src/utils/group-async.ts b/src/utils/group-async.ts new file mode 100644 index 00000000..74b5b237 --- /dev/null +++ b/src/utils/group-async.ts @@ -0,0 +1,57 @@ +/** + * @file Utilities - groupAsync + * @module tutils/utils/groupAsync + */ + +import type { Mapper, PropertyKey } from '#src/types' +import cast from './cast' +import isUndefined from './is-undefined' +import reduceAsync from './reduce-async' + +/** + * Group each item in an array. + * + * The return value is a plain object with a key for each group, where each key + * value is an array containing group items. + * + * A `key` function is used to map array items to group keys. + * + * @see {@linkcode Mapper} + * + * @todo examples + * + * @async + * + * @template T - Array to group + * @template K - Identity key type + * + * @param {T} arr - Array to group + * @param {Mapper>} key - Group key mapper + * @return {Promise>} Groups object + */ +const groupAsync = async < + T extends readonly unknown[], + K extends PropertyKey = PropertyKey +>( + arr: T, + key: Mapper> +): Promise<{ [H in K]: T[number][] }> => { + return reduceAsync(arr, async (acc, item, i) => { + /** + * Group key. + * + * @const {K} k + */ + const k: K = await key(item, i, arr) + + // initialize group + isUndefined(acc[k]) && (acc[k] = []) + + // add group item + acc[k].push(item) + + return acc + }, cast({})) +} + +export default groupAsync diff --git a/src/utils/index.ts b/src/utils/index.ts index ba61352e..4d726a69 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -32,6 +32,7 @@ export { default as flat } from './flat' export { default as fork } from './fork' export { default as get } from './get' export { default as group } from './group' +export { default as groupAsync } from './group-async' export { default as hasOwn } from './has-own' export { default as identity } from './identity' export { default as ifelse } from './ifelse'