154 lines
4.2 KiB
JavaScript
154 lines
4.2 KiB
JavaScript
// Arrays. The compiler gives only `a[i]`, `a[i] = x` and `a.length`, which can
|
|
// also be written. Every method here is built on those
|
|
//
|
|
// Not as the spec says:
|
|
// - A method takes one value where the spec takes any number (`push(x)`,
|
|
// `unshift(x)`, `concat(other)`), because more need rest params
|
|
// - A callback gets the element and its index, not the array too
|
|
// - Holes are not skipped: a callback sees undefined for a hole
|
|
|
|
// `Array(n)` and `new Array(n)` make an array of `n` holes
|
|
function Array(n) {
|
|
const a = [];
|
|
if (n !== undefined) a.length = n;
|
|
return a;
|
|
}
|
|
|
|
Array.prototype = {
|
|
// ── Change the array ──
|
|
push(x) {
|
|
this[this.length] = x;
|
|
return this.length;
|
|
},
|
|
pop() {
|
|
if (this.length === 0) return undefined;
|
|
const x = this[this.length - 1];
|
|
this.length = this.length - 1;
|
|
return x;
|
|
},
|
|
shift() {
|
|
if (this.length === 0) return undefined;
|
|
const x = this[0];
|
|
for (let i = 1; i < this.length; i++) this[i - 1] = this[i];
|
|
this.length = this.length - 1;
|
|
return x;
|
|
},
|
|
unshift(x) {
|
|
for (let i = this.length; i > 0; i--) this[i] = this[i - 1];
|
|
this[0] = x;
|
|
return this.length;
|
|
},
|
|
reverse() {
|
|
for (let i = 0, j = this.length - 1; i < j; i++, j--) {
|
|
const x = this[i];
|
|
this[i] = this[j];
|
|
this[j] = x;
|
|
}
|
|
return this;
|
|
},
|
|
fill(x, start, end) {
|
|
const len = this.length;
|
|
const s = relativeIndex(start === undefined ? 0 : start, len);
|
|
const e = relativeIndex(end === undefined ? len : end, len);
|
|
for (let i = s; i < e; i++) this[i] = x;
|
|
return this;
|
|
},
|
|
|
|
// ── Read ──
|
|
at(i) {
|
|
if (i < 0) i += this.length;
|
|
return this[i];
|
|
},
|
|
indexOf(x, from) {
|
|
for (let i = from === undefined ? 0 : relativeIndex(from, this.length); i < this.length; i++) {
|
|
if (this[i] === x) return i;
|
|
}
|
|
return -1;
|
|
},
|
|
lastIndexOf(x) {
|
|
for (let i = this.length - 1; i >= 0; i--) {
|
|
if (this[i] === x) return i;
|
|
}
|
|
return -1;
|
|
},
|
|
includes(x) {
|
|
return this.indexOf(x) !== -1;
|
|
},
|
|
join(sep) {
|
|
const s = sep === undefined ? "," : sep;
|
|
let out = "";
|
|
for (let i = 0; i < this.length; i++) {
|
|
if (i > 0) out = out + s;
|
|
const x = this[i];
|
|
if (x !== undefined && x !== null) out = out + String(x);
|
|
}
|
|
return out;
|
|
},
|
|
toString() {
|
|
return this.join(",");
|
|
},
|
|
|
|
// ── Make a new array ──
|
|
slice(start, end) {
|
|
const len = this.length;
|
|
const s = relativeIndex(start === undefined ? 0 : start, len);
|
|
const e = relativeIndex(end === undefined ? len : end, len);
|
|
const out = [];
|
|
for (let i = s; i < e; i++) out[i - s] = this[i];
|
|
return out;
|
|
},
|
|
concat(other) {
|
|
const out = this.slice();
|
|
for (let i = 0; i < other.length; i++) out[out.length] = other[i];
|
|
return out;
|
|
},
|
|
|
|
// ── With a callback ──
|
|
forEach(f) {
|
|
for (let i = 0; i < this.length; i++) f(this[i], i);
|
|
},
|
|
map(f) {
|
|
const out = [];
|
|
for (let i = 0; i < this.length; i++) out[i] = f(this[i], i);
|
|
return out;
|
|
},
|
|
filter(f) {
|
|
const out = [];
|
|
for (let i = 0; i < this.length; i++) {
|
|
if (f(this[i], i)) out[out.length] = this[i];
|
|
}
|
|
return out;
|
|
},
|
|
reduce(f, init) {
|
|
let acc = init;
|
|
for (let i = 0; i < this.length; i++) acc = f(acc, this[i], i);
|
|
return acc;
|
|
},
|
|
find(f) {
|
|
for (let i = 0; i < this.length; i++) {
|
|
if (f(this[i], i)) return this[i];
|
|
}
|
|
return undefined;
|
|
},
|
|
findIndex(f) {
|
|
for (let i = 0; i < this.length; i++) {
|
|
if (f(this[i], i)) return i;
|
|
}
|
|
return -1;
|
|
},
|
|
some(f) {
|
|
for (let i = 0; i < this.length; i++) {
|
|
if (f(this[i], i)) return true;
|
|
}
|
|
return false;
|
|
},
|
|
every(f) {
|
|
for (let i = 0; i < this.length; i++) {
|
|
if (!f(this[i], i)) return false;
|
|
}
|
|
return true;
|
|
},
|
|
};
|
|
|
|
__link_proto_array(Array.prototype);
|