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

SurveyJS custom survey software

Build Your Own Forms without Manual Coding

SurveyJS UI libraries let you build a JSON-based form management system that integrates with any backend, giving you full control over your data with no user limits. Includes support for custom question types, skip logic, an integrated CSS editor, PDF export, real-time analytics, and more.

Learn more

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more