-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Creating external folder * Extracting compare to a new internal file and reusing it * Name change * Group by method * groupByUnique * Exporting group by
- Loading branch information
1 parent
36c3ee7
commit 5d3aa72
Showing
7 changed files
with
393 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export type RecordKeyType = string | number | symbol | ||
|
||
export type KeysMatching<T extends object, V> = { | ||
[K in keyof T]: T[K] extends V ? K : never | ||
}[keyof T] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 1 addition & 4 deletions
5
packages/app/universal-ts-utils/src/public/array/sortByField.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
179 changes: 179 additions & 0 deletions
179
packages/app/universal-ts-utils/src/public/object/groupBy.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import { describe, expect, it } from 'vitest' | ||
import { groupBy } from './groupBy' | ||
|
||
describe('groupBy', () => { | ||
it('Empty array', () => { | ||
const array: { id: string }[] = [] | ||
const result = groupBy(array, 'id') | ||
expect(Object.keys(result)).length(0) | ||
}) | ||
|
||
type TestType = { | ||
id?: number | null | ||
name: string | ||
bool: boolean | ||
nested?: { | ||
code: number | ||
} | ||
} | ||
|
||
it('Correctly groups by string values', () => { | ||
const input: TestType[] = [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
{ | ||
id: 2, | ||
name: 'c', | ||
bool: true, | ||
nested: { code: 200 }, | ||
}, | ||
{ | ||
id: 3, | ||
name: 'b', | ||
bool: true, | ||
nested: { code: 300 }, | ||
}, | ||
{ | ||
id: 4, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 400 }, | ||
}, | ||
] | ||
|
||
const result: Record<string, TestType[]> = groupBy(input, 'name') | ||
expect(result).toStrictEqual({ | ||
a: [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
{ | ||
id: 4, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 400 }, | ||
}, | ||
], | ||
b: [ | ||
{ | ||
id: 3, | ||
name: 'b', | ||
bool: true, | ||
nested: { code: 300 }, | ||
}, | ||
], | ||
c: [ | ||
{ | ||
id: 2, | ||
name: 'c', | ||
bool: true, | ||
nested: { code: 200 }, | ||
}, | ||
], | ||
}) | ||
}) | ||
|
||
it('Correctly groups by number values', () => { | ||
const input: TestType[] = [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
}, | ||
{ | ||
id: 1, | ||
name: 'b', | ||
bool: false, | ||
}, | ||
{ | ||
id: 2, | ||
name: 'c', | ||
bool: false, | ||
}, | ||
{ | ||
id: 3, | ||
name: 'd', | ||
bool: false, | ||
}, | ||
] | ||
|
||
const result: Record<number, TestType[]> = groupBy(input, 'id') | ||
|
||
expect(result).toStrictEqual({ | ||
1: [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
}, | ||
{ | ||
id: 1, | ||
name: 'b', | ||
bool: false, | ||
}, | ||
], | ||
2: [ | ||
{ | ||
id: 2, | ||
name: 'c', | ||
bool: false, | ||
}, | ||
], | ||
3: [ | ||
{ | ||
id: 3, | ||
name: 'd', | ||
bool: false, | ||
}, | ||
], | ||
}) | ||
}) | ||
|
||
it('Correctly handles undefined and null', () => { | ||
const input: TestType[] = [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
}, | ||
{ | ||
name: 'c', | ||
bool: true, | ||
}, | ||
{ | ||
id: null, | ||
name: 'd', | ||
bool: true, | ||
}, | ||
{ | ||
id: 1, | ||
name: 'b', | ||
bool: true, | ||
}, | ||
] | ||
|
||
const result = groupBy(input, 'id') | ||
|
||
expect(result).toStrictEqual({ | ||
1: [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
}, | ||
{ | ||
id: 1, | ||
name: 'b', | ||
bool: true, | ||
}, | ||
], | ||
}) | ||
}) | ||
}) |
29 changes: 29 additions & 0 deletions
29
packages/app/universal-ts-utils/src/public/object/groupBy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import type { KeysMatching, RecordKeyType } from '../../internal/types' | ||
|
||
/** | ||
* @param array The array of objects to be grouped. | ||
* @param selector The key used for grouping the objects. | ||
* @returns An object where the keys are unique values from the given selector and the values are the corresponding objects from the array. | ||
*/ | ||
export const groupBy = < | ||
T extends object, | ||
K extends KeysMatching<T, RecordKeyType | null | undefined>, | ||
>( | ||
array: T[], | ||
selector: K, | ||
): Record<RecordKeyType, T[]> => { | ||
return array.reduce( | ||
(acc, item) => { | ||
const key = item[selector] as RecordKeyType | null | undefined | ||
if (key === undefined || key === null) { | ||
return acc | ||
} | ||
if (!acc[key]) { | ||
acc[key] = [] | ||
} | ||
acc[key].push(item) | ||
return acc | ||
}, | ||
{} as Record<RecordKeyType, T[]>, | ||
) | ||
} |
147 changes: 147 additions & 0 deletions
147
packages/app/universal-ts-utils/src/public/object/groupByUnique.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { describe, expect, it } from 'vitest' | ||
import { groupByUnique } from './groupByUnique' | ||
|
||
describe('groupByUnique', () => { | ||
it('Empty array', () => { | ||
const array: { id: string }[] = [] | ||
const result = groupByUnique(array, 'id') | ||
expect(Object.keys(result)).length(0) | ||
}) | ||
|
||
type TestType = { | ||
id?: number | null | ||
name: string | ||
bool: boolean | ||
nested: { | ||
code: number | ||
} | ||
} | ||
|
||
it('Correctly groups by string values', () => { | ||
const input: TestType[] = [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
{ | ||
id: 2, | ||
name: 'b', | ||
bool: true, | ||
nested: { code: 200 }, | ||
}, | ||
] | ||
|
||
const result: Record<string, TestType> = groupByUnique(input, 'name') | ||
expect(result).toStrictEqual({ | ||
a: { | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
|
||
b: { | ||
id: 2, | ||
name: 'b', | ||
bool: true, | ||
nested: { code: 200 }, | ||
}, | ||
}) | ||
}) | ||
|
||
it('Correctly groups by number values', () => { | ||
const input: TestType[] = [ | ||
{ | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
{ | ||
id: 2, | ||
name: 'b', | ||
bool: true, | ||
nested: { code: 200 }, | ||
}, | ||
] | ||
|
||
const result: Record<number, TestType> = groupByUnique(input, 'id') | ||
|
||
expect(result).toStrictEqual({ | ||
1: { | ||
id: 1, | ||
name: 'a', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
2: { | ||
id: 2, | ||
name: 'b', | ||
bool: true, | ||
nested: { code: 200 }, | ||
}, | ||
}) | ||
}) | ||
|
||
it('Correctly handles undefined', () => { | ||
const input: TestType[] = [ | ||
{ | ||
id: 1, | ||
name: 'name', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
{ | ||
name: 'invalid', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
{ | ||
id: 3, | ||
name: 'name 2', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
] | ||
|
||
const result = groupByUnique(input, 'id') | ||
|
||
expect(result).toStrictEqual({ | ||
1: { | ||
id: 1, | ||
name: 'name', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
3: { | ||
id: 3, | ||
name: 'name 2', | ||
bool: true, | ||
nested: { code: 100 }, | ||
}, | ||
}) | ||
}) | ||
|
||
it('throws on duplicated value', () => { | ||
const input: { name: string }[] = [ | ||
{ | ||
id: 1, | ||
name: 'test', | ||
}, | ||
{ | ||
id: 2, | ||
name: 'work', | ||
}, | ||
{ | ||
id: 3, | ||
name: 'test', | ||
}, | ||
] as never[] | ||
|
||
expect(() => groupByUnique(input, 'name')).toThrowError( | ||
'Duplicated item for selector name with value test', | ||
) | ||
}) | ||
}) |
Oops, something went wrong.