Right here we go. Ones that don’t make sense (eg reduce
itself, toString
, etc, etc) are commented out. I just realised I forgot flatMap
, but I should be doing work anyway, so hey ho. I haven’t tested all of them so there may be a few slight errors.
One thing to note is how I’ve exited the loop where necessary: reduce
et al are designed to iterate through an entire array. But the fourth argument to the reduce
callback is the actual array currently being iterated over. By mutating it using splice
when I want to break out of it, I can change the length (thisArr.splice(1)
). Because the length has been changed and there are no more entries, reduce will finish iterating. Note that this will actually mutate the input array, so this is an awful hack that won’t generally be useful in practice, it’s just used as an example here because I didn’t have time to think of anything better.
- no mutator methods (push, pop, shift, unshift or splice). These are used within the
reduce
function
- no reflection-like methods (
toSource
etc) as they don’t make sense in this context.
- no
sort
as I’m not writing a sort implementation
- no
reduce
or reduceRight
as that defeats the point (however… *)
Just to clarify, don’t actually do any of this
, it’s all pretty awful (though on the whole I think it’s fairly efficient), use the defined methods
function at(arr, n) {
if (n < 0) {
n = arr.length + n;
}
return arr.reduce((acc, v, i, thisArr) => {
if (i === n) {
thisArr.splice(1);
return v;
} else {
return acc;
}
}, undefined);
}
// function concat() {}
function copyWithin(arr, target, start = 0, end = arr.length) {
let replaceCount = end - start;
return arr.reduce((acc, v, i) => {
if (i >= target && replaceCount !== 0) {
acc[i] = arr[end - replaceCount];
replaceCount--
} else {
acc[i] = v;
}
return acc;
}, []);
}
function entries(arr) {
return arr.reduce((acc, v, i) => {
acc.push([i, v]);
return acc;
}, []);
}
function every(arr, cb) {
return arr.reduce((acc, v, _i, thisArr) => {
if (!cb(v)) {
thisArr.splice(1);
return false;
}
return acc;
}, true);
}
function fill(arr, val, start = 0, end = arr.length) {
// NOTE slightly incomplete: I haven't handled negative values:
return arr.reduce((acc, v, i) => {
if (i >= start && i < end) {
acc[i] = val;
} else {
acc[i] = v;
}
return acc;
}, []);
}
function filter(arr, cb) {
return arr.reduce((acc, v) => {
if (cb(v)) {
acc.push(v);
}
return acc;
}, [])
}
function find(arr, cb) {
return arr.reduce((acc, v, _i, thisArr) => {
if (cb(v)) {
thisArr.splice(1);
return v;
}
return acc;
}, undefined);
}
function findIndex(arr, cb) {
return arr.reduce((acc, v, i, thisArr) => {
if (cb(v)) {
thisArr.splice(1);
return i;
}
return acc;
}, -1);
}
function flat(arr, depth = 1) {
if (depth > 0) {
return arr.reduce((acc, val) => {
if (Array.isArray(val)) {
acc.push(flat(val, depth - 1));
} else {
acc.push(val);
}
return acc;
}, [])
} else {
return slice(arr);
}
}
function flatMap(arr, cb) {}
function forEach(arr, cb) {
return arr.reduce((acc, val) => {
cb(val);
return acc;
}, undefined);
}
function includes(arr, val) {
return arr.reduce((acc, v, _i, thisArr) => {
if (v === val) {
thisArr.splice(1);
return true;
} else {
return acc;
}
}, false)
}
function indexOf(arr, val) {
return arr.reduce((acc, v, i, thisArr) => {
if (v === val) {
thisArr.splice(1);
return i;
} else {
return acc;
}
}, -1)
}
function join(arr, joiner = ",") {
return arr.reduce((acc, v, i) => {
if (i === 0) {
return `${v}`;
} else {
return `${acc}${joiner}${v}`;
}
}, "")
}
function keys(arr) {
return arr.reduce((acc, _v, i) => {
acc.push(i);
return acc;
}, [])
}
function lastIndexOf(arr, val) {
return arr.reduce((acc, v, i) => {
if (v === val) {
return i;
} else {
return acc;
}
}, -1)
}
function map(arr, cb) {
return arr.reduce((acc, v) => {
acc.push(cb(v));
return acc;
}, [])
}
// function pop() {}
// function push() {}
// function reduce() {}
// function reduceRight() {}
function reverse(arr) {
return arr.reduce((acc, v) => {
acc.unshift(v);
return acc;
}, []);
}
// function shift() {}
function slice(arr, start = 0, end = arr.length) {
if (start < 0) {
start = end + start;
}
return arr.reduce((acc, v, i, thisArr) => {
if (i >= end) {
thisArr.splice(1);
} else if (i >= start) {
acc.push(v);
}
return acc;
}, [])
}
function some(arr, cb) {
return arr.reduce((acc, v, _i, thisArr) => {
if (cb(v)) {
thisArr.splice(1);
return true;
} else {
return acc;
}
}, false)
}
// function sort() {}
// function splice() {}
// function toLocaleString() {}
// function toSource() {}
// function toString() {}
// function unshift() {}
function values(arr) {
return arr.reduce((acc, v) => {
acc.push(v);
return acc
}, []);
}
* bonus. I think this is robust – anyway all the above can use this instead of the Array.prototype.reduce
, although doing that would be an even more terrible idea:
function reduce(arr, cb, acc, i = 0) {
const [head, ...tail] = arr;
if (head === undefined) {
return acc;
} else {
return reduce(tail, cb, cb(acc, head, i, arr), i + 1);
}
}