Array.prototype - JavaScript Problems
Implementing Array.prototype methods
You can find the code at: JavaScript Problems - Github.
In JavaScript, you can extend the Array methods simply by adding functions to the `Array.prototype` object.
It is worth noting that:
Use the `
this` keyword within the function body to access the array object itself.For sparse array, the empty values should be ignored while traversing.
Use `
Object.hasOwn()`to check if an index actually exists so that we can handle sparse array properly.If the method needs to return an array, don’t mutate the original array. Instead, create a new array to store the returned elements. Typically, use
`Array.prototype.map()`,`[…this]`.Use
`Array.isArray()`to check array type.When invoke the
`.call()`method on callback function, the order of parameters is:`thisArg`element
index
array
Array.prototype.last
Write code that enhances all arrays such that you can call the `
array.last()`method on any array and it will return the last element. If there are no elements in the array, it should return `-1`. - LeetCode
Solutions
`Array.prototype.at(-1)`Return the item with last index
this.length - 1.
/**
* @return {null|boolean|number|string|Array|Object}
*/
Array.prototype.myLast = function() {
return this.length ? this.at(-1) : -1;
// return this.length ? this[this.length - 1] : -1;
};Array.prototype.at
The
`.at()` method of `Array`instances takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array. - MDN
Solution
Use the modulo operation `%` to support the negative index.
/**
* @param {number} index
* @return {any | undefined}
*/
Array.prototype.myAt = function (index) {
const len = this.length;
if (index < -len || index >= len) {
return;
}
return this[(index + len) % len];
};
Array.prototype.concat
The `
concat()`method of `Array`instances is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array. - MDN
Solution
Copy the original array and `.push()` every items of the parameter array.
/**
* @template T
* @param {...(T | Array<T>)} itemes
* @return {Array<T>}
*/
Array.prototype.myConcat = function (...items) {
const newArray = [...this];
for (const item of items) {
if (Array.isArray(item)) {
newArray.push(...item);
} else {
newArray.push(item);
}
}
return newArray;
}Array.prototype.square
Implement a custom
Arrayfunction, `Array.prototype.square()`which creates a new array with the results of squaring every element within the array the `.square()`method is called on.
Solutions
`Array.prototype.map()`Copy the original array, use
`for`loop to traverse and square the items.
/**
* @return {Array<number>}
*/
// s1
Array.prototype.mySquare = function () {
return this.map((el) => el * el);
}
// s2
Array.prototype.mySquare = function () {
const len = this.length;
const newArray = new Array(len);
for (let i = 0; i < len; i += 1) {
newArray[i] = this[i] * this[i];
}
return newArray;
}Array.prototype.forEach
The `
forEach()`method of `Array`instances executes a provided function once for each array element. - MDN
Solution
The idea is to iterate over the array and execute a callback function for each element.
To follow the ECMA specific, use:
`Object(this)`to ensure`this`is an object`O.length >>> 0`to ensure`length`is not negative
/**
* @template T, U
* @param { (value: T, index: number, array: Array<T>) => U } callbackFn
* @param {any} [thisArg]
* @return {Array<U>}
*/
Array.prototype.myForEach = function (callbackFn, thisArg) {
if (!this) {
throw new TypeError('this is null or not defined');
}
if (typeof callbackFn !== 'function') {
throw new TypeError(callbackFn + 'is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
for (let i = 0; i < len; i += 1) {
if (Object.hasOwn(O, i)) {
callbackFn.call(thisArg, O[i], i, O);
}
}
}Array.prototype.map
The `.
map()`method of `Array`instances creates a new array populated with the results of calling a provided function on every element in the calling array. - MDN
Solution
Create a new array. Iterate over the original array and execute a callback function for each element, then push the results to the new array.
/**
* @template T, U
* @param { (value: T, index: number, array: Array<T>) => U } callbackFn
* @param {any} [thisArg]
* @return {Array<U>}
*/
Array.prototype.myMap = function (callbackFn, thisArg) {
const len = this.length;
const newArray = new Array(len);
for (let i = 0; i < len; i += 1) {
if (Object.hasOwn(this, i)) {
newArray[i] = callbackFn.call(thisArg, this[i], i, this);
}
}
return newArray;
}Array.prototype.filter
The `.
filter()`method ofArrayinstances creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test implemented by the provided function. - MDN
Solution
The implementation is similar to `.map()`.
Create a new array. Iterate over the original array and execute a callback function for each element, only push the elements which fulfill the callback to the new array.
/**
* @template T, U
* @param { (value: T, index: number, array: Array<T>) => boolean } callbackFn
* @param { any } [thisArg]
* @return {Array<T>}
*/
Array.prototype.myFilter = function (callbackFn, thisArg) {
const newArray = [];
for (let i = 0; i < this.length; i += 1) {
if (
Object.hasOwn(this, i) &&
callbackFn.call(thisArg, this[i], i, this)
) {
newArray.push(this[i]);
}
}
return newArray;
}Array.prototype.reduce
The `.
reduce()`method of `Array`instances executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value. - MDN
Solution
The implementation is similar to `.map()` and `.filter()` with the following difference:
We don’t use
`.call()`,`.apply()`,`.bind()`to bind the callback function with a given object since`thisArg`is not in the parameter list.
/**
* @template T, U
* @param { (previousValue: U, currentValue: T, currentIndex: number, array: Array<T>) => U } callbackFn
* @param {U} [initialValue]
* @return {U}
*/
Array.prototype.myReduce = function (callbackFn, initialValue) {
const hasInitialValue = initialValue !== undefined;
const len = this.length;
if (!hasInitialValue && !len) {
throw new Error('Reduce of empty array with no initial value');
}
let accumulator = hasInitialValue ? initialValue : this[0];
let startingIndex = hasInitialValue ? 0 : 1;
for (let i = startingIndex; i < len; i += 1) {
if (Object.hasOwn(this, i)) {
accumulator = callbackFn(accumulator, this[i], i, this);
}
}
return accumulator;
}Array.prototype.every
The `.
every()`method of `Array`instances tests whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value. - MDN
Solution
Check if all elements in the array satisfy a provided test function and use a flag variable to return.
/**
* @template T
* @param { (value: T, index: number, array: Array<T>) => boolean } callbackFn
* @param {any} [thisArg]
* @return {boolean}
*/
Array.prototype.myEvery = function (callbackFn, thisArg) {
const len = this.length;
let flag = true;
for (let i = 0; i < len; i += 1) {
if (
Object.hasOwn(this, i) &&
!callbackFn.call(thisArg, this[i], i, this)
) {
flag = false;
break;
}
}
return flag;
}Array.prototype.some
The `.
some()`method of `Array`instances tests whether at least one element in the array passes the test implemented by the provided function. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false. It doesn't modify the array. - MDN
Solution
The implementation is similar to `.every()`.
Check if all elements in the array satisfy a provided test function and use a flag variable to return.
/**
* @template T
* @param { (value: T, index: number, array: Array<T>) => boolean } callbackFn
* @param {any} [thisArg]
* @return {boolean}
*/
Array.prototype.mySome = function (callbackFn, thisArg) {
const len = this.length;
let flag = false;
for (let i = 0; i < len; i += 1) {
if (
Object.hasOwn(this, i) &&
callbackFn.call(thisArg, this[i], i, this)
) {
flag = true;
break;
}
}
return flag;
}
