Array.prototype.groupBy
Add myGroupBy(fn) to Array.prototype.
fn returns the key for each item. The result is an object whose values are arrays of the items sharing that key, in their original order.
Examples
[1, 2, 3].myGroupBy((n) => (n % 2 ? 'odd' : 'even'));{ odd: [1, 3], even: [2] }Constraints
- 0 <= array.length <= 10^5
- fn returns a string key
Notes
- `out[key] = out[key] || []` breaks when a key is "constructor" or "toString", which are truthy on every object.
Source
Hints
Array.prototype.groupBy (reference solution)
One way to do it. The Editorial tab walks through why it is written this way.
Solution
1Array.prototype.myGroupBy = function (fn) {
2 const out = {};
3 for (let i = 0; i < this.length; i += 1) {
4 const key = fn(this[i], i, this);
5 if (!Object.prototype.hasOwnProperty.call(out, key)) out[key] = [];
6 out[key].push(this[i]);
7 }
8 return out;
9};Editorial: Array.prototype.groupBy
Bucketing
Grouping is the operation reduce was made for, and it is now a real language feature: Object.groupBy and Map.groupBy ship in modern browsers. Implementing it by hand is still asked because the failure mode is interesting.
Approach
For each item, compute the key, ensure a bucket, push.
Implementation
Array.prototype.myGroupBy = function (fn) { const out = {}; for (let i = 0; i < this.length; i += 1) { const key = fn(this[i], i, this); if (!Object.prototype.hasOwnProperty.call(out, key)) out[key] = []; out[key].push(this[i]); } return out; };
Worth knowing
out[key] = out[key] || [] looks correct and is not. Every plain object inherits constructor, toString and valueOf from its prototype, and all of them are truthy — so grouping by a key that happens to be "constructor" finds a truthy value and calls .push on a function. hasOwnProperty avoids it, and Object.create(null) avoids it structurally by making an object with no prototype at all.
Order inside a bucket must match the input, so walk forwards and append.