DEV Community

Mitchell
Mitchell

Posted on • Edited on

Array prototype - JavaScript Challenges

You can find all the code in this post in the repo Github.


Array prototype related challenges

Something to notice:

  • Use this within the function to access the original array.
  • For some methods, copy the original array first and then modify the copied array.
  • When iterating over the array, use Object.hasOwn() to check if the index exists.
  • The parameters order of the callback function is: (element, index, array).

Array.prototype.at()

Use (index + len) % len to support a negative index.

/**
 * @param {number} index
 * @return {any | undefiend}
 */

// Time: O(1) | Space: O(1)
Array.prototype.myAt = function (index) {
  const len = this.length;

  if (index < -len || index >= len) {
    return;
  }

  return this[(index + len) % len];
};

// Usage example
console.log([1, 2, 3, 4].myAt(2)); // => 3
console.log([1, 2, 3, 4].myAt(-1)); // => 4
console.log([1, 2, 3, 4].myAt(5)); // => undefined
Enter fullscreen mode Exit fullscreen mode

Array.prototype.concat()

Copy the original array first and add the rest arrays.

/**
 * @template T
 * @param {...(T | Array<T>)} itemes
 * @return {Array<T>}
 */

// Time: O(n) | Space: O(n)
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;
};

// Usage example
console.log([1, 2, 3].myConcat([])); // => [1, 2, 3];
console.log([1, 2, 3].myConcat([4, 5, 6, [2]])); // => [1, 2, 3, 4, 5, 6, [2]];
Enter fullscreen mode Exit fullscreen mode

Array.prototype.every()

Use a flag variable.

/**
 * @template T
 * @param { (value: T, index: number, array: Array<T>) => boolean } callbackFn
 * @param {any} [thisArg]
 * @return {boolean}
 */

// Time: O(n) | Space: O(1)
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;
};

// Usage example
console.log([1, 2, 3].myEvery((item) => item > 2)); // => false
console.log([1, 2, 3].myEvery((item) => item > 0)); // => true
Enter fullscreen mode Exit fullscreen mode

Array.prototype.filter()

Use the callback function as the conditional filter function.

/**
 * @template T, U
 * @param { (value: T, index: number, array: Array<T>) => boolean } callbackFn
 * @param { any } [thisArg]
 * @return {Array<T>}
 */

// Time: O(n) | Space: O(n)
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;
};

// Usage example
console.log([1, 2, 3, 4].myFilter((value) => value % 2 == 0)); // => [2, 4]
console.log([1, 2, 3, 4].myFilter((value) => value < 3)); // => [1, 2]
Enter fullscreen mode Exit fullscreen mode

Array.prototype.flat()

Recursively flatten the given array based on the depth.

/**
 * @param { Array } arr
 * @param { number } depth
 * @returns { Array }
 */

// Time: O(n) | Space: O(n)
function flatten(arr, depth = 1) {
  const newArray = [];

  for (let i = 0; i < arr.length; i += 1) {
    if (Array.isArray(arr[i]) && depth !== 0) {
      newArray.push(...flatten(arr[i], depth - 1));
    } else {
      newArray.push(arr[i]);
    }
  }

  return newArray;
}

// Usage example
const array = [[1, 2], [1], 1, [[[1]]]];
console.log(flatten(array)); // => [ 1, 2, 1, 1, [ [ 1 ] ] ]
Enter fullscreen mode Exit fullscreen mode

Array.prototype.flatMap()

/**
 * @param {functioon} callbackFn
 * @param {object | undefined} thisArg
 * @return {array}
 */

// Time: O(1) | Space: O(1)
Array.prototype.myFlatMap = function (callbackFn, thisArg) {
  return this.reduce((result, element, index, array) => {
    const mappedValue = callbackFn.call(thisArg, element, index, array);
    return result.concat(mappedValue);
  }, []);
};

// Usage example
const arr1 = [1, 2, 1];
const result = arr1.myFlatMap((num) => (num === 2 ? [2, 2] : 1));
console.log(result); // => [1, 2, 2, 1];
Enter fullscreen mode Exit fullscreen mode

Array.prototype.forEach()

Invoking callback function on every item in the array.

/**
 * @template T, U
 * @param { (value: T, index: number, array: Array<T>) => U } callbackFn
 * @param {any} [thisArg]
 * @return {Array<U>}
 */

// Time: O(n) | Space: O(1)
Array.prototype.myForEach = function (callbackFn, thisArg) {
  if (this == null) {
    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);
  // Zero-fill Right Shift to ensure that the result if always non-negative.
  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);
    }
  }
};

// Usage example
console.log(
  [1, 2, 3].myForEach((el) => el * el),
  null
); // => [1, 4, 9];
Enter fullscreen mode Exit fullscreen mode

Array.prototype.indexOf()

Iterates over the array to find the index of the first occurrence of the specified element.

/**
 * @param {any} searchElement
 * @param {number} fromIndex
 * @return {number}
 */

// Time: O(n) | Space: O(1)
Array.prototype.myIndexOf = function (searchElement, fromIndex = 0) {
  const len = this.length;

  if (fromIndex < 0) {
    fromIndex = Math.max(0, fromIndex + this.length);
  }

  for (let i = fromIndex; i < len; i += 1) {
    if (this[i] === searchElement) {
      return i;
    }
  }

  return -1;
};

// Usage example
console.log([1, 2, 3, 4, 5].myIndexOf(3)); // => 2
console.log([1, 2, 3, 4, 5].myIndexOf(6)); // => -1
console.log([1, 2, 3, 4, 5].myIndexOf(1)); // => 0
console.log(["a", "b", "c"].myIndexOf("b")); // => 1
console.log([NaN].myIndexOf(NaN)); // => -1 (since NaN !== NaN)
Enter fullscreen mode Exit fullscreen mode

Array.prototype.last()

Return the last element in the array.

/**
 * @return {null|boolean|number|string|Array|Object}
 */

// Time: O(1) | Space: O(1)
Array.prototype.myLast = function () {
  return this.length ? this.at(-1) : -1;
  // or
  // return this.length ? this[this.length - 1] : -1;
};

// Usage example
console.log([].myLast()); // => -1;
console.log([1].myLast()); // => 1
console.log([1, 2].myLast()); // => 2
Enter fullscreen mode Exit fullscreen mode

Array.prototype.map()

Similar to .forEach() but returns a new array.

/**
 * @template T, U
 * @param { (value: T, index: number, array: Array<T>) => U } callbackFn
 * @param {any} [thisArg]
 * @return {Array<U>}
 */

// Time: O(n) | Space: O(n)
Array.prototype.myMap = function (callbackFn, thisArg) {
  const len = this.length;
  const newArray = Array.from({ length: 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;
};

// Usage example
console.log([1, 2, 3, 4].myMap((i) => i)); // => [1, 2, 3, 4]
console.log([1, 2, 3, 4].myMap((i) => i * i)); // => [1, 4, 9, 16])
Enter fullscreen mode Exit fullscreen mode

Array.prototype.reduce()

Invoking the callback function on the accumulator.

/**
 * @template T, U
 * @param { (previousValue: U, currentValue: T, currentIndex: number, array: Array<T>) => U } callbackFn
 * @param {U} [initialValue]
 * @return {U}
 */

// Time: O(n) | Space: O(1)
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;
};

// Usage example
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.myReduce((acc, num) => acc + num, 0);
console.log(sum); // => 15
const products = numbers.myReduce((acc, num) => acc * num, 1);
Enter fullscreen mode Exit fullscreen mode

Array.prototype.sample()

/**
 * @return {any}
 */

// Time: O(1) | Space: O(1)
Array.prototype.mySample = function () {
  const randIdx = Math.floor(Math.random() * this.length);

  return this[randIdx];
};

// Usage example
const arr = [1, 2, 3, 4, 5, 6, 7];
console.log(arr.mySample()); // => *
Enter fullscreen mode Exit fullscreen mode

Array.prototype.snail()

It's a reverse-problem of Spiral Matrix of LeetCode.

/**
 * @param {number} rowsCount
 * @param {number} colsCount
 * @return {Array<Array<number>>}
 */

// Time: O(n) | Space: O(n^2)
Array.prototype.snail = function (rowsCount, colsCount) {
  if (this.length === 0 || rowsCount * colsCount !== this.length) {
    return [];
  }

  const result = Array.from({ length: rowsCount }, () => {
    return Array.from({ length: colsCount }, () => 0);
  });
  let isReversed = false;

  for (let i = 0; i < this.length; i += 1) {
    const row = !isReversed ? i % rowsCount : rowsCount - 1 - (i % rowsCount);
    const col = Math.floor(i / rowsCount);

    result[row][col] = this[i];

    if (i % rowsCount === rowsCount - 1) {
      isReversed = !isReversed;
    }
  }

  return result;
};

// Usage example
const arr = [
  19, 10, 3, 7, 9, 8, 5, 2, 1, 17, 16, 14, 12, 18, 6, 13, 11, 20, 4, 15,
];
console.log(arr.snail(5, 4));
/*
[
  [19,17,16,15],
  [10,1,14,4],
  [3,2,12,20],
  [7,5,18,11],
  [9,8,6,13]
]
*/
Enter fullscreen mode Exit fullscreen mode

Array.prototype.some()

The solution structure is similar to .every().
Use a flag variable.

/**
 * @template T
 * @param { (value: T, index: number, array: Array<T>) => boolean } callbackFn
 * @param {any} [thisArg]
 * @return {boolean}
 */

// Time: O(n) | Space: O(1)
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;
};

// Usage example
console.log([1, 2, 3].mySome((item) => item > 2)); // => true
console.log([1, 2, 3].mySome((item) => item < 0)); // => false
Enter fullscreen mode Exit fullscreen mode

Array.prototype.square()

The solution structure is similar to .map().

/**
 * @return {Array<number>}
 */

// Time: O(n) | Space: O(n)
Array.prototype.mySquare = function () {
  const len = this.length;
  const newArray = Array.from({ length: len });

  for (let i = 0; i < len; i += 1) {
    newArray[i] = this[i] * this[i];
  }

  return newArray;
};

// Usage example
console.log([1, 2, 3].mySquare()); // => [1, 4, 9];
console.log([].mySquare()); // => [];
Enter fullscreen mode Exit fullscreen mode

Reference

Top comments (0)