Implement Array Methods
Interview Question: "Implement map, filter, reduce, forEach, and flat from scratch."
Array.prototype.map
Array.prototype.myMap = function(callback, thisArg) {
const result = new Array(this.length);
for (let i = 0; i < this.length; i++) {
if (i in this) {
result[i] = callback.call(thisArg, this[i], i, this);
}
}
return result;
};Key details:
- Returns a new array of the same length
callback.call(thisArg, ...)â the second argument tomapsetsthisinside the callbacki in thisâ skips holes in sparse arrays:[1, , 3].map(x => x)preserves the hole- Callback receives
(element, index, array)
Array.prototype.filter
Array.prototype.myFilter = function(callback, thisArg) {
const result = [];
for (let i = 0; i < this.length; i++) {
if (i in this && callback.call(thisArg, this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};Key differences from map:
- Result array can be shorter than input
- Only pushes elements where callback returns truthy
Array.prototype.reduce
Array.prototype.myReduce = function(callback, initialValue) {
let accumulator;
let startIndex;
if (arguments.length >= 2) {
accumulator = initialValue;
startIndex = 0;
} else {
if (this.length === 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
let found = false;
for (let i = 0; i < this.length; i++) {
if (i in this) {
accumulator = this[i];
startIndex = i + 1;
found = true;
break;
}
}
if (!found) {
throw new TypeError('Reduce of empty array with no initial value');
}
}
for (let i = startIndex; i < this.length; i++) {
if (i in this) {
accumulator = callback(accumulator, this[i], i, this);
}
}
return accumulator;
};Critical edge cases:
- No
initialValue+ empty array âTypeError - No
initialValueâ first non-hole element becomes accumulator, iteration starts from next element - Reduce does NOT take a
thisArg(unlike map/filter) - Sparse array holes are skipped
[1, 2, 3].myReduce((acc, val) => acc + val); // 6
[1, 2, 3].myReduce((acc, val) => acc + val, 10); // 16
[].myReduce((acc, val) => acc + val); // TypeErrorArray.prototype.forEach
Array.prototype.myForEach = function(callback, thisArg) {
for (let i = 0; i < this.length; i++) {
if (i in this) {
callback.call(thisArg, this[i], i, this);
}
}
};Key point: forEach returns undefined â it cannot be chained. It also cannot be stopped early (no break). Use a for...of loop if you need early exit.
Array.prototype.flat
Array.prototype.myFlat = function(depth = 1) {
const result = [];
function flatten(arr, currentDepth) {
for (let i = 0; i < arr.length; i++) {
if (!(i in arr)) continue;
if (Array.isArray(arr[i]) && currentDepth < depth) {
flatten(arr[i], currentDepth + 1);
} else {
result.push(arr[i]);
}
}
}
flatten(this, 0);
return result;
};Alternative iterative approach:
Array.prototype.myFlat = function(depth = 1) {
let result = [...this];
for (let d = 0; d < depth; d++) {
const next = [];
let didFlatten = false;
for (const item of result) {
if (Array.isArray(item)) {
next.push(...item);
didFlatten = true;
} else {
next.push(item);
}
}
result = next;
if (!didFlatten) break;
}
return result;
};[1, [2, [3, [4]]]].myFlat(); // [1, 2, [3, [4]]]
[1, [2, [3, [4]]]].myFlat(2); // [1, 2, 3, [4]]
[1, [2, [3, [4]]]].myFlat(Infinity); // [1, 2, 3, 4]Follow-Up: reduceRight
Array.prototype.myReduceRight = function(callback, initialValue) {
let accumulator;
let startIndex;
if (arguments.length >= 2) {
accumulator = initialValue;
startIndex = this.length - 1;
} else {
if (this.length === 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = this[this.length - 1];
startIndex = this.length - 2;
}
for (let i = startIndex; i >= 0; i--) {
if (i in this) {
accumulator = callback(accumulator, this[i], i, this);
}
}
return accumulator;
};Common Mistakes
- Forgetting
thisArgsupport (second argument to map/filter/forEach) - Not handling sparse arrays (
i in thischeck) - Using
arguments.lengthinstead of checkinginitialValue !== undefinedfor reduce (the latter fails whenundefinedis explicitly passed as initial value) - Not handling
Infinitydepth forflat - Missing
TypeErrorfor reduce on empty array without initial value