@tienedev/datype
Version:
Modern TypeScript utility library with pragmatic typing and zero dependencies
92 lines (89 loc) • 2.98 kB
JavaScript
'use strict';
/**
* Groups an array of items by a key or the result of an iterator function.
*
* @template T - The type of items in the array
* @template K - The type of the grouping key
* @param array - The array to group
* @param iteratee - A property key or function to determine the grouping key
* @returns An object where keys are the group identifiers and values are arrays of items
*
* @example
* ```typescript
* import { groupBy } from 'datype';
*
* // Group by property
* const users = [
* { name: 'John', role: 'admin', age: 30 },
* { name: 'Jane', role: 'user', age: 25 },
* { name: 'Bob', role: 'admin', age: 35 }
* ];
*
* const byRole = groupBy(users, 'role');
* // {
* // admin: [{ name: 'John', role: 'admin', age: 30 }, { name: 'Bob', role: 'admin', age: 35 }],
* // user: [{ name: 'Jane', role: 'user', age: 25 }]
* // }
*
* // Group by function result
* const byAgeGroup = groupBy(users, user => user.age >= 30 ? 'senior' : 'junior');
* // {
* // senior: [{ name: 'John', role: 'admin', age: 30 }, { name: 'Bob', role: 'admin', age: 35 }],
* // junior: [{ name: 'Jane', role: 'user', age: 25 }]
* // }
*
* // Group numbers by remainder
* const numbers = [1, 2, 3, 4, 5, 6];
* const byRemainder = groupBy(numbers, n => n % 2);
* // { 1: [1, 3, 5], 0: [2, 4, 6] }
*
* // Group strings by length
* const words = ['cat', 'dog', 'elephant', 'bee'];
* const byLength = groupBy(words, word => word.length);
* // { 3: ['cat', 'dog', 'bee'], 8: ['elephant'] }
*
* // Group by nested property
* const products = [
* { name: 'Laptop', category: { type: 'electronics', subtype: 'computers' } },
* { name: 'Phone', category: { type: 'electronics', subtype: 'mobile' } },
* { name: 'Book', category: { type: 'media', subtype: 'books' } }
* ];
*
* const byType = groupBy(products, product => product.category.type);
* // {
* // electronics: [...],
* // media: [...]
* // }
* ```
*/
function groupBy(array, iteratee) {
if (!Array.isArray(array)) {
throw new TypeError('Expected first argument to be an array');
}
if (typeof iteratee !== 'string' &&
typeof iteratee !== 'number' &&
typeof iteratee !== 'symbol' &&
typeof iteratee !== 'function') {
throw new TypeError('Expected iteratee to be a property key or function');
}
const result = {};
for (let i = 0; i < array.length; i++) {
const item = array[i];
let key;
if (typeof iteratee === 'function') {
key = iteratee(item);
}
else {
const value = item?.[iteratee];
key = value;
}
const safeKey = String(key === null || key === undefined ? String(key) : key);
if (!result[safeKey]) {
result[safeKey] = [];
}
result[safeKey].push(item);
}
return result;
}
// Note: Overloads are handled by the main function signature above
exports.groupBy = groupBy;