You'll implement groupBy, a function that takes an array and a way to derive a key from each element, then returns an object whose values are arrays of elements that share the same key. It's the same shape as lodash's _.groupBy — useful any time you want to bucket a list (orders by status, files by extension, transactions by month).
type Iteratee<T> = ((value: T) => unknown) | string;
function groupBy<T>(
array: T[],
iteratee: Iteratee<T>
): Record<string, T[]>;
The iteratee is either a function called with each element, or a string property key looked up on each element. Whatever it returns becomes the bucket name (coerced to a string).
// Function iteratee — bucket by floor
groupBy([1.2, 1.4, 2.1, 2.7], Math.floor);
// → { '1': [1.2, 1.4], '2': [2.1, 2.7] }
// Property-key iteratee — bucket by the value at obj[key]
const people = [
{ name: 'Ada', team: 'eng' },
{ name: 'Grace', team: 'eng' },
{ name: 'Linus', team: 'ops' },
];
groupBy(people, 'team');
// → { eng: [{name:'Ada',...}, {name:'Grace',...}], ops: [{name:'Linus',...}] }
Math.floor returning 2 becomes the key '2'. An iteratee returning undefined produces the bucket 'undefined'.groupBy([], anything) returns {}.iteratee is a string, treat it as a property name: the bucket key is element[iteratee]. You can ignore symbol keys.