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

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay