diff --git a/src/shared/lib/groupByKey.test.ts b/src/shared/lib/groupByKey.test.ts new file mode 100644 index 0000000..fad0754 --- /dev/null +++ b/src/shared/lib/groupByKey.test.ts @@ -0,0 +1,54 @@ +import { groupByKey } from './groupByKey'; + +describe('groupByKey', () => { + describe('basic grouping', () => { + it('groups items by a string key', () => { + const items = [ + { category: 'Frontend', name: 'React' }, + { category: 'Backend', name: 'Node' }, + { category: 'Frontend', name: 'Vue' }, + ]; + expect(groupByKey(items, 'category')).toEqual({ + Frontend: [ + { category: 'Frontend', name: 'React' }, + { category: 'Frontend', name: 'Vue' }, + ], + Backend: [{ category: 'Backend', name: 'Node' }], + }); + }); + + it('preserves insertion order within each group', () => { + const items = [ + { category: 'A', order: 1 }, + { category: 'A', order: 2 }, + ]; + expect(groupByKey(items, 'category')['A']).toEqual([ + { category: 'A', order: 1 }, + { category: 'A', order: 2 }, + ]); + }); + }); + + describe('edge cases', () => { + it('returns empty object for empty array', () => { + expect(groupByKey<{ category: string }>([], 'category')).toEqual({}); + }); + + it('handles all items in same group', () => { + const items = [ + { type: 'X', id: 1 }, + { type: 'X', id: 2 }, + ]; + const result = groupByKey(items, 'type'); + expect(Object.keys(result)).toHaveLength(1); + expect(result['X']).toHaveLength(2); + }); + + it('handles single item', () => { + const items = [{ category: 'Only', name: 'One' }]; + expect(groupByKey(items, 'category')).toEqual({ + Only: [{ category: 'Only', name: 'One' }], + }); + }); + }); +}); diff --git a/src/shared/lib/groupByKey.ts b/src/shared/lib/groupByKey.ts new file mode 100644 index 0000000..1919981 --- /dev/null +++ b/src/shared/lib/groupByKey.ts @@ -0,0 +1,19 @@ +/** + * Groups an array of objects by a shared key into a record of arrays. + * @param items - Array of objects to group + * @param key - Key whose value determines the group + * @returns Record mapping each unique key value to an array of matching items + */ +export function groupByKey(items: T[], key: keyof T): Record { + return items.reduce( + (acc, item) => { + const k = String(item[key]); + if (!acc[k]) { + acc[k] = []; + } + acc[k].push(item); + return acc; + }, + {} as Record, + ); +} diff --git a/src/shared/lib/index.ts b/src/shared/lib/index.ts index e8331fc..3342b79 100644 --- a/src/shared/lib/index.ts +++ b/src/shared/lib/index.ts @@ -2,3 +2,4 @@ export type { ClassValue } from 'clsx'; export { cn } from './cn'; export * from './fonts'; export * from './formatDate'; +export { groupByKey } from './groupByKey';