Skip to content

Commit

Permalink
feat(utils): groupAsync
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Sep 14, 2023
1 parent 9a8b86d commit 34179be
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 9 deletions.
19 changes: 19 additions & 0 deletions src/utils/__tests__/group-async.spec-d.ts
Original file line number Diff line number Diff line change
@@ -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<Record<K, T[number][]>>', () => {
// Arrange
type T = Vehicle[]
type K = Vehicle['year']
type Expect = Promise<Record<K, T[number][]>>

// Expect
expectTypeOf<typeof testSubject<T, K>>().returns.toEqualTypeOf<Expect>()
})
})
15 changes: 15 additions & 0 deletions src/utils/__tests__/group-async.spec.ts
Original file line number Diff line number Diff line change
@@ -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]
})
})
})
12 changes: 3 additions & 9 deletions src/utils/__tests__/group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,9 @@ import testSubject from '../group'

describe('unit:utils/group', () => {
it('should return groups object', () => {
// Arrange
const cases: [...Parameters<typeof testSubject<number[]>>, 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]
})
})
})
57 changes: 57 additions & 0 deletions src/utils/group-async.ts
Original file line number Diff line number Diff line change
@@ -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<T, K | Promise<K>>} key - Group key mapper
* @return {Promise<Record<K, T[number][]>>} Groups object
*/
const groupAsync = async <
T extends readonly unknown[],
K extends PropertyKey = PropertyKey
>(
arr: T,
key: Mapper<T, K | Promise<K>>
): 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
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down

0 comments on commit 34179be

Please sign in to comment.