I wrote polyfills for 32 JavaScript Array methods!

I wrote polyfills for 32 JavaScript Array methods!

Introduction

While working on projects, I used many JavaScript methods. I was curious about their implementation. So, I tried to implement some of them and it was fun! Fast forward to today, I wrote polyfills for 32 JavaScript Array methods.

In this article, I’ve shared my custom implementation of the JavaScript methods.

While implementing, I’ve divided the methods into 6 categories:

  1. Searching methods
  2. Manipulation(Mutating) methods
  3. Iterative methods
  4. Map, filter, and reduce methods
  5. Returning array iterator methods
  6. Other operations methods

array methods

PS: There might be some improvements that can be made or some test cases that I’ve missed addressing in the custom implementations. You can create an issue in this repository if you find any improvement that can be made. Thanks!

Searching methods:

1. at()

at() method accepts an integer and returns the element present at that index in the array

Test cases:

  1. Positive integer
    • returns the element present at that index
  2. Negative integer
    • counts the element from the last index and returns the element present at that index
  3. Integer which is out of range of the array length
    • returns undefined

Custom implementation:

Array.prototype.myAt = function (index) {
  if (index >= 0) {
    return this[index];
  } else {
    return this[this.length + index];
  }
};

Output:

const arr = ["a", "b", "c", "d", "e"];

// Test case 1:
console.log(arr.myAt(3));
//  output: d 

// Test case 2:
console.log(arr.myAt(-1));
// output: e

// Test case 3:
console.log(arr.myAt(7));
// output: undefined

2. indexOf()

indexOf() method accepts an element and returns the first index at which the element is present in the array

Test cases:

  1. Element found
    • returns the index of the first element where the given element is present
  2. Element not found
    • returns -1

Custom implementation:

Array.prototype.myIndexOf = function (element, fromIndex = 0) {
  for (let i = fromIndex; i < this.length; i++) {
    if (this[i] === element) {
      return i;
    }
  }
  return -1;
};

Output:

const arr = ["a", "b", "c", "d", "e", "a"];

// Test case 1:
console.log(arr.myIndexOf("a"));
//  output: 0 

// Test case 2:
console.log(arr.myIndexOf("z"));
// output: -1

3. lastIndexOf()

lastIndexOf() method accepts an element and returns the last index at which the element is present in the array

Test cases:

  1. Element found
    • returns the index of the last element where the given element is present
  2. Element not found
    • returns -1

Custom implementation:

Array.prototype.myLastIndexOf = function (
  element,
  fromIndex = this.length - 1
) {
  for (let i = fromIndex; i >= 0; i--) {
    if (this[i] === element) {
      return i;
    }
  }
  return -1;
};

Output:

const arr = ["a", "b", "c", "d", "e", "a"];

// Test case 1:
console.log(arr.myLastIndexOf("a"));
//  output: 5 

// Test case 2:
console.log(arr.myLastIndexOf("z"));
//  output: -1

4 find()

  • find() method accepts a callback testing function which is executed on every element of the array.
  • The first element in the given array that fulfills the testing function is returned by the find() method.

Test cases:

  1. Element found
    • returns the first element for which the callback function returns a truthy value
  2. Element not found
    • returns undefined

Custom Implementation:

Array.prototype.myFind = function (cb) {
  for (let i = 0; i < this.length; i++) {
    if (cb(this[i], i, this)) {
      return this[i];
    }
  }
};

Output:

const arr = [10, 20, 31, 44, 55, 67];

const result1 = arr.myFind((element) => element % 20 === 0);
console.log(result1);
// output: 20

const result2 = arr.myFind((element) => element % 3 === 0);
console.log(result2);
// output: undefined

5 findIndex()

  • findIndex() method accepts a testing callback function that executes on every element of the array
  • The index of the first element in the given array that fulfills the testing function is returned by the find() method.

Test cases:

  1. Element found
    • returns the index of the first element for which the callback function returns a truthy value
  2. Element not found
    • returns -1, if the callback function doesn’t return a truthy value for any element in the array

Custom Implementation:

Array.prototype.myFindIndex = function (cb) {
  for (let i = 0; i < this.length; i++) {
    if (cb(this[i], i, this)) {
      return i;
    }
  }
  return -1;
};

Output:

const arr = [10, 20, 31, 44, 55, 67];

const result1 = arr.myFindIndex((element) => element % 20 === 0);
console.log(result1);
// output: 1

const result2 = arr.myFindIndex((element) => element % 3 === 0);
console.log(result2);
// output: -1

6 findLast()

  • findLast() method accepts a testing callback function that executes on every element of the array
  • The last element in the given array that fulfills the testing function is returned by the find() method.

Test cases:

  1. Element found
    • returns the last element for which the callback function returns a truthy value
  2. Element not found
    • returns undefined

Custom Implementation:

Array.prototype.myFindLast = function (cb) {
  for (let i = this.length; i >= 0; i--) {
    if (cb(this[i], i, this)) {
      return this[i];
    }
  }
};

Output:

const arr = [10, 20, 31, 44, 55, 67];

const result1 = arr.myFindLast((element) => element % 5 === 0);
console.log(result1);
// output: 55

const result2 = arr.myFindLast((element) => element % 3 === 0);
console.log(result2);
// output: undefined

7 findLastIndex()

  • findLastIndex() method accepts a callback function that executes on every element of the array
  • The index of the last element in the given array that fulfills the testing function is returned by the find() method.

Test cases:

  1. Element found
    • returns the index of the last element for which the callback function returns a truthy value
  2. Element not found
    • returns -1, if the callback function doesn’t return a truthy value for any element in the array

Custom Implementation:

Array.prototype.myFindLastIndex = function (cb) {
  for (let i = this.length; i >= 0; i--) {
    if (cb(this[i], i, this)) {
      return i;
    }
  }
  return -1;
};

Output:

const arr = [10, 20, 31, 44, 55, 67];

const result1 = arr.myFindLastIndex((element) => element % 5 === 0);
console.log(result1);
// output: 4

const result2 = arr.myFindLastIndex((element) => element % 3 === 0);
console.log(result2);
// output: -1

8 includes()

includes() checks whether the given element is present in the array if it is present, the method returns true, else false.

Test cases:

  1. Element found
    • returns true
  2. Element not found
    • returns false

Custom implementation:

Array.prototype.myIncludes = function (element, fromIndex = 0) {
  for (let i = fromIndex; i < this.length; i++) {
    if (this[i] === element) {
      return true;
    }
  }
  return false;
};

Output:

const arr = ["a", "b", "c", "d", "e", "a"];

// Test case 1:
const result1 = arr.myIncludes("a");
console.log(result1)
// output: true

// Test case 2:
const result2 = arr.myIncludes("z");
console.log(result2)
// output: false

9 some()

  • some() method accepts a callback function that executes on every element of the array
  • it returns true if at least one element in the array returns a truthy value for the callback function provided.

Test cases:

  1. At least one element found
    • returns true
  2. No element found
    • returns false

Custom Implementation:

Array.prototype.mySome = function (cb) {
  for (let i = 0; i < this.length; i++) {
    if (cb(this[i])) {
      return true;
    }
  }
  return false;
};

Output:

// Test case 1:
const arr1 = [10, 21, 32, 43, 54, 65];
const hasEven = arr1.mySome((el) => el % 2 === 0);
console.log(hasEven);
// output: true 

// Test case 2:
const arr2 = [10, 20, 30, 40, 50, 60];
const hasOdd = arr2.mySome((el) => el % 2 !== 0);
console.log(hasOdd);
// output: false

10 every()

  • every() method accepts a callback function that executes on every element of the array
  • It returns true if every element in the array returns a truthy value for the callback function provided

Test cases:

  1. Every element found
    • returns true
  2. Some elements found
    • returns false
  3. No element found
    • returns false

Custom Implementation:

Array.prototype.myEvery = function (cb) {
  for (let i = 0; i < this.length; i++) {
    if (!cb(this[i])) {
      return false;
    }
  }
  return true;
};

Output:

// Test case 1
const arr1 = [10, 20, 30, 40, 50, 60];
const isAllEven = arr1.myEvery((el) => el % 2 === 0)
console.log(isAllEven);
// output: true 

// Test case 2
const arr2 = [10, 21, 30, 41, 50, 60];
const isAllEven1 = arr2.myEvery((el) => el % 2 === 0)
console.log(isAllEven1);
// output: false

// Test case 3
const arr3 = [11, 21, 30, 41, 51, 61];
const isAllEven2 = arr3.myEvery((el) => el % 2 === 0)
console.log(isAllEven2);
// output: false

Iterative:

11 forEach()

  • forEach() accepts a callback function and executes it on every element.
  • returns undefined
  • the callback function is not invoked on uninitialized elements

Test cases:

  1. Array with all elements:
    • the callback function is executed on every element
  2. Sparse array
    • the callback function is not executed on uninitialized elements

Custom implementation:

Array.prototype.myForEach = function (cb) {
  for (let i = 0; i < this.length; i++) {
    if (typeof this[i] !== "undefined") {
      cb(this[i], i, this);
    }
  }
};

Output:

// Test case 1:
const arr = ["a", "b", "c"];

arr.myForEach((element, index) => console.log({[index]: element}));
// output:
// {0: 'a'}
// {1: 'b'}
// {2: 'c'}

// Test case 2:
const arr1 = ["a", , "c"];

arr1.myForEach((element, index) => console.log({[index]: element}));
// output:
// {0: 'a'}
// {2: 'c'}

map, filter, reduce

12 map()

  • map() method accepts a callback function that executes on every element of the array
  • it returns a new array
  • the new array contains the return values of the callback function that is executed on every element of the array

Custom implementation:

Array.prototype.myMap = function (cb) {
  let result = [];

  for (let i = 0; i < this.length; i++) {
    const value = cb(this[i], i, this);
    result.push(value);
  }

  return result;
};

Output:

const arr = [1, 2, 3, 4, 5];

const result = arr.myMap((el) => el * 2);

console.log(result);
// output: [2, 4, 6, 8, 10];

13 filter()

  • filter() method accepts a callback function that executes on every element of the array
  • it returns a new array
  • the new array contains only those elements for which the callback function returns a truthy value

Custom implementation:

Array.prototype.myFilter = function (cb) {
  let result = [];

  for (let i = 0; i < this.length; i++) {
    if (cb(this[i], i, this)) {
      result.push(this[i]);
    }
  }

  return result;
};

Output:

const arr = [1, 2, 3, 4, 5];

const result = arr.myFilter((el) => el % 2 === 0);

console.log(result);
// output: [2, 4];

14 reduce()

  • reduce() method accepts a callback function called the reducer.
  • The reducer is executed on every element of the array and reduces it to a single value.
  • The reducer function has four arguments: previous value, current value, the current index, and the array

Test cases:

  1. Without passing the initial value
  2. Passing initial value

Custom implementation:

Array.prototype.myReduce = function (cb, initialValue) {
  let result;
  let startIndex = 0;

  if (arguments.length <= 1) {
    result = this[0];
    startIndex = 1;
  }

  if (arguments.length >= 2) {
    result = initialValue;
  }

  for (let i = startIndex; i < this.length; i++) {
    result = cb(result, this[i]);
  }

  return result;
};

Output:

const arr = [1, 2, 3, 4, 5];

// Test case 1:
const sum = arr.myReduce((prev, curr) => prev + curr);
console.log(sum);
// output: 15

// Test case 2:
const oddEvenCount = arr.myReduce(
      (prev, curr) =>
        curr % 2 == 0
          ? { ...prev, even: prev.even + 1 }
          : { ...prev, odd: prev.odd + 1 },
      { even: 0, odd: 0 }
    );
console.log(oddEvenCount)
// output: {even: 2, odd: 3};

15 reduceRight()

reduceRight() is the same as the reduce() method, the only difference being that it executes the reducer function backward from the last element of the array.

Test cases:

  1. Without passing the initial value
  2. Passing initial value

Custom implementation:

Array.prototype.myReduceRight = function (cb, initialValue) {
  let result;
  let startIndex = this.length - 1;

  if (arguments.length <= 1) {
    result = this[this.length - 1];
    startIndex = this.length - 2;
  }

  if (arguments.length >= 2) {
    result = initialValue;
  }

  for (let i = startIndex; i >= 0; i--) {
    result = cb(result, this[i]);
  }

  return result;
};

Output:

const arr = [1, 2, 3, 4, 5];

// Test case 1:
const sum = arr.myReduceRight((prev, curr) => prev + curr);
console.log(sum);
// output: 15

// Test case 2:
const oddEvenCount = arr.myReduceRight(
      (prev, curr) =>
        curr % 2 == 0
          ? { ...prev, even: prev.even + 1 }
          : { ...prev, odd: prev.odd + 1 },
      { even: 0, odd: 0 }
    );
console.log(oddEvenCount)
// output: {even: 2, odd: 3};

Manipulating methods:

16 unshift()

  • unshift() adds elements to the start of the array.
  • It mutates the original array.
  • It returns the new length of the array.

Custom implementation:

Array.prototype.myUnshift = function () {
  if (arguments.length > 0) {
    // move elements of the array ahead
    for (let i = this.length - 1; i >= 0; i--) {
      this[i + arguments.length] = this[i];
    }

    // add the args elements at the start
    for (let i = 0; i < arguments.length; i++) {
      this[i] = arguments[i];
    }
  }

  return this.length;
};

Output:

const array = ["a", "b", "c", "d", "e"];
const result = array.myUnshift("z", "y", "p");

console.log(result)
// 8

console.log(array)
// ["z", "y", "p", "a", "b", "c", "d", "e"];

17 pop()

  • pop() removes the last element of the array.
  • It mutates the original array.
  • It returns the removed element.

Test cases:

  1. empty array
    • returns undefined
  2. non-empty array
    • returns the new length of the array

Custom implementation:

Array.prototype.myPop = function () {
  if (this.length > 0) {
    let lastEl = this[this.length - 1];
    this.length -= 1;
    return lastEl;
  }
};

Output:

// Test case 1:
const array = ["a", "b", "c", "d", "e"];
const result = array.myPop();
console.log(result); // "e"
console.log(array); // ["a", "b", "c", "d"];

// Test case 2:
const emptyArray = [];
const result1 = emptyArray.myPop();
console.log(result1); // undefined
console.log(emptyArray); // [];

18 push()

  • push() adds elements to the end of the array.
  • It mutates the original array.
  • It returns the new length of the array.

Custom implementation:

Array.prototype.myPush = function () {
  for (let i = 0; i < arguments.length; i++) {
    this[this.length] = arguments[i];
  }
  return this.length;
};

Output:

const array = ["a", "b", "c", "d", "e"];
const result = array.myPush("z", "y", "p");

console.log(result)
// 8

console.log(array)
// ["a", "b", "c", "d", "e", "z", "y", "p"];

19 shift()

  • shift() removes the first element of the array.
  • It mutates the original array.
  • It returns the removed element.

Test cases:

  1. empty array
    • returns undefined
  2. non-empty array
    • returns the new length of the array

Custom implementation:

Array.prototype.myShift = function () {
  if (this.length > 0) {
    const firstEl = this[0];

    for (let i = 0; i < this.length; i++) {
      this[i] = this[i + 1];
    }

    this.length -= 1;

    return firstEl;
  }
};

Output:

// Test case 1:
const array = ["a", "b", "c", "d", "e"];
const result = array.myShift();
console.log(result); // "a"
console.log(array); // ["b", "c", "d", "e"];

// Test case 2:
const emptyArray = [];
const result1 = emptyArray.myShift();
console.log(result1); // undefined
console.log(emptyArray); // [];

20 fill()

  • fill() fills or replaces elements in an array with a given value.
  • It accepts three arguments: value, start, and, end.
  • It fills the elements between start and end with the given value.

Test cases:

  1. No start and end provided
    • fills all the elements with the value given.
  2. start provided, end not provided
    • fills all the elements starting from the start index with the value given.
  3. start and end both provided
    • fills all the elements between start and end with the value given. (element at the end index is not included)
  4. Negative indexes:
    • counts the index backward from the last element of the array

Custom implementation:

Array.prototype.myFill = function (value, start = 0, end = this.length) {
  if (start < 0) {
    start = this.length + start;
  }

  if (end < 0) {
    end = this.length + end;
  }

  for (let i = start; i < end; i++) {
    this[i] = value;
  }

  return this;
};

Output:

// Test case 1:
const arr1 = ["a", "b", "c", "d", "e"];
const result1 = arr1.myFill("*");
console.log(result1)
// ["*", "*", "*", "*", "*"];

// Test case 2:
const arr2 = ["a", "b", "c", "d", "e"];
const result2 = arr2.myFill("*", 1);
console.log(result2)
// ["a", "*", "*", "*", "*"];

// Test case 3:
const arr3 = ["a", "b", "c", "d", "e"];
const result3 = arr3.myFill("*", 1, 3);
console.log(result3)
// ["a", "*", "*", "d", "e"];

// Test case 4:
const arr4 = ["a", "b", "c", "d", "e"];
const result4 = arr4.myFill("*", -4, -1);
console.log(result4)
// ["a", "*", "*", "*", "e"];

21 reverse()

reverse() method reverses the array. It mutates the original array and returns the reversed array

Custom implementation:

Array.prototype.myReverse = function () {
  let start = 0,
    end = this.length - 1;

  while (start < end) {
    let temp = this[start];
    this[start] = this[end];
    this[end] = temp;
    start++;
    end--;
  }

  return this;
};

Output:

const arr = [1, 2, 3, 4, 5];
const result = arr.myReverse();

console.log(arr); // [5, 4, 3, 2, 1]
console.log(result); // [5, 4, 3, 2, 1]

22 sort()

  • sort() method sorts the array.
  • By default, it converts the elements to strings and sorts them in ascending order.
  • We can provide a comparator function to sort the elements in a custom order.

Test cases:

  1. comparator function not provided
    • It converts the elements to strings and sorts them in ascending order.
  2. comparator function provided
    • It sorts the elements based on the comparator function

Custom implementation:

function defaultComparator(a, b) {
  a = a.toString();
  b = b.toString();
  if (a < b) {
    return -1;
  }
  if (a > b) {
    return 1;
  }
  return 0;
}

Array.prototype.mySort = function (cb = defaultComparator) {
  for (var i = 0; i < this.length; i++) {
    for (var j = i + 1; j < this.length; j++) {
      if (cb(this[i], this[j]) > 0) {
        var swap = this[i];
        this[i] = this[j];
        this[j] = swap;
      }
    }
  }
};

Output:

// Test case 1:
const arr = [30, 55, 1, 3, 66, 22];
arr.mySort();
console.log(arr);
// output: [1, 22, 3, 30, 55, 66];

// Test case 1:
const arr1 = [30, 55, 1, 3, 66, 22];
arr1.mySort((a, b) => a - b);
console.log(arr1);
// output: [1, 3, 22, 30, 55, 66];

23 copyWithin()

copyWithin() copies a portion of the array to another location within the same array. It accepts three arguments: target, start, and end.

Test cases:

  1. target provided, start and end not provided
    • It will copy the element at the 0th index to the target index
  2. target and start provided, end not provided
    • It will copy the element present at the start index to the target index
  3. target, start, and, end provided
    • It will copy the elements lying between the start and end to the target index. (element present at the end index will not be included)

Custom implementation:

Array.prototype.myCopyWithin = function (
  target = 0,
  start = 0,
  end = this.length
) {
  if (target < 0) {
    target = this.length + target;
  }

  if (start < 0) {
    start = this.length + start;
  }

  if (end < 0) {
    end = this.length + end;
  }

  for (let i = start; i < end && target < this.length; i++) {
    this[target] = this[i];
    target++;
  }

  return this;
};

Output:

// Test Case 1:
const array1 = ['a', 'b', 'c', 'd', 'e'];
array1.myCopyWithin(4); 
console.log(array1)
// output: ['a', 'b', 'c', 'd', 'a']

const array2 = ['a', 'b', 'c', 'd', 'e'];
array2.myCopyWithin(3, 1); 
console.log(array2)
// output: ['a', 'b', 'c', 'b', 'c']

const array3 = ['a', 'b', 'c', 'd', 'e', 'f'];
array3.myCopyWithin(0, 3, 5); 
console.log(array3)
// output: ['d', 'e', 'c', 'd', 'e', 'f']

Returning iterator methods

24 keys()

keys() creates and returns an Array Iterator object with the key for each item in the array.

Custom implementation:

Array.prototype.myKeys = function () {
  let keys = [];
  for (let i = 0; i < this.length; i++) {
    keys.push(i);
  }

  function* iterator() {
    yield* keys;
  }

  return iterator();
};

Output:

const arr = ["a", "b", "c", "d", "e"];

const keys = arr.myKeys();

console.log(keys.next().value);
//output: 0

for (let key of keys) {
        console.log(key);
}

/**
output:
1
2
3
4
**/

25 values()

values() creates and returns an Array Iterator object with the value for each item in the array.

Custom implementation:

Array.prototype.myValues = function () {
  let keys = [];
  for (let i = 0; i < this.length; i++) {
    keys.push(this[i]);
  }

  function* iterator() {
    yield* keys;
  }

  return iterator();
};

Output:

const arr = ["a", "b", "c", "d", "e"];

const values = arr.myValues();

console.log(values.next().value);
//output: a

for (let value of values) {
        console.log(value);
}

/**
output:
a
b
c
d
e
**/

26 entries()

entries() creates and returns an Array Iterator object with key-value pairs for each item in the array.

Custom implementation:

Array.prototype.myEntries = function () {
  let keys = [];
  for (let i = 0; i < this.length; i++) {
    keys.push([i, this[i]]);
  }

  function* iterator() {
    yield* keys;
  }

  return iterator();
};

Output:

const arr = ["a", "b", "c", "d", "e"];

const entries = arr.myEntries();

console.log(entries.next().value);
//output: [0, 'a']

for (let entry of entries) {
        console.log(entry);
}

/**
output:
[1, 'b']
[2, 'c']
[3, 'd']
[4, 'e']
**/

Other methods:

27 slice()

  • slice() is used to slice the array and get a portion of the array.
  • It accepts the start and end index as arguments and returns the portion of the array lying between them.

Test cases:

  1. Positive start index
    • Returns the array elements starting from the start index
  2. Positive start and end index
    • Returns the portion array lying between the start and end index. (the element lying on the end index is not included)
  3. Negative start index
    • The index is counted backward from the end.
  4. Negative start and end index
    • The index is counted backward from the end. Returns the portion array lying between the start and end index. (the element lying on the end index is not included)

Custom implementation:

Array.prototype.mySlice = function (start = 0, end = this.length) {
  if (start < 0) {
    start = this.length + start;
  }

  if (end < 0) {
    end = this.length + end;
  }

  let arr = [];
  for (let i = start; i < end; i++) {
    arr.push(this[i]);
  }

  return arr;
};

Output:

const arr = 
["a", "b", "c", "d", "e"];

// Test case 1:
const result1 = arr.mySlice(2);
console.log(result1);
// output: ["c", "d", "e"]

// Test case 2:
const result2 = arr.mySlice(2, 4);
console.log(result2);
// output: ["c", "d"]

// Test case 3:
const result3 = arr.mySlice(-1);
console.log(result3);
// output: ["e"]

// Test case 4:
const result4 = arr.mySlice(-5, -2);
console.log(result4);
// output: ["a", "b", "c"]

28 concat()

concat() method merges two or more arrays or values.

Test cases:

  1. merging two arrays
    1. returns the merger array
  2. merging a variable with an array
    1. merges the variable to the array and returns the merged array
  3. merging three arrays
    1. merges the three arrays in the order given and returns the merged array

Custom implementation:

Array.prototype.myConcat = function () {
  let newArr = [];

  for (let i = 0; i < this.length; i++) {
    newArr.push(this[i]);
  }

  for (let i = 0; i < arguments.length; i++) {
    if (Array.isArray(arguments[i])) {
      const dummyArr = arguments[i];
      for (let i = 0; i < dummyArr.length; i++) {
        newArr.push(dummyArr[i]);
      }
    } else {
      newArr.push(arguments[i]);
    }
  }

  return newArr;
};

Output:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [7, 8, 9];

// Test case 1:
const result1 = arr1.myConcat(arr2);
console.log(result1);
// output: [1, 2, 3, 4, 5, 6]

// Test case 2:
const result2 = arr1.myConcat("a");
console.log(result2);
// output: [1, 2, 3, "a"]

// Test case 3:
const result3 = arr1.myConcat(arr2, arr3);
console.log(result3);
// output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

29 flat()

flat() methods flatten an array up to a given depth. Flattens here means it concatenates the sub-array elements into the original array. It returns the flattened array.

Test cases:

  1. no depth specified
    • flattens till depth 1
  2. depth argument passed
    • flattens till the given depth

Custom implementation:

Array.prototype.myFlat = function (depth = 1) {
  const result = [];

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

  return result;
};

Array.prototype.myDeepFlat = function () {
  let result = [];

  (function flatten(arr) {
    for (let i = 0; i < arr.length; i++) {
      if (Array.isArray(arr[i])) {
        flatten(arr[i]);
      } else {
        result.push(arr[i]);
      }
    }
  })(this);

  return result;
};

Output:

const arr = [1, 2, 3, 4, 5];

// Test Case 1:
const arr1 = [10, 20, ["a", "b"]];
const result1 = arr1.myFlat();
console.log(result1);
// output: [10, 20, "a", "b"];

// Test Case 2:
const arr2 = [10, 20, [["a", "b"]]];;
const result2 = arr2.myFlat(2);
console.log(result2);
// output: [10, 20, "a", "b"];

30 flatMap()

flatMap() accepts a callback function. It executes the callback function on every element of the array, flattens the result to depth 1, and returns the newly formed array

Custom implementation:

Array.prototype.myFlatMap = function (cb) {
  let result = [];

  for (let i = 0; i < this.length; i++) {
    let cbRes = cb(this[i], i, this);

    if (Array.isArray(cbRes)) {
      for (let i = 0; i < cbRes.length; i++) {
        result.push(cbRes[i]);
      }
    } else {
      result.push(cbRes);
    }
  }

  return result;
};

Output:

const arr = [10, 21, 30, 42, 50];

// Test case 1: 
const result1 = arr.myFlatMap((el) => (el % 10 === 0 ? [] : [el]));
console.log(result1);
// output: [21, 42];

// Test case 2: 
const result2 = arr.myFlatMap((el) => el);
console.log(result2);
// output: [10, 21, 30, 42, 50];

31 join()

join() method concatenates the array and returns a string.

Test cases:

  1. no separator argument passed:
    • concatenates element using , a comma as a separator
  2. separator passed
    • concatenates elements using the separator provided

Custom implementation:

Array.prototype.myJoin = function (separator = ",") {
  let result = "";

  for (let i = 0; i < this.length; i++) {
    if (i === this.length - 1) {
      result += this[i];
    } else {
      result += this[i] + separator;
    }
  }

  return result;
};

Output:

const arr = [1, 2, 3, 4, 5];

// Test Case 1:
const commaSeperatedString = arr.myJoin();
console.log(commaSeperatedString);
// output: 1,2,3,4,5

// Test Case 2:
const result = arr.myJoin("*");
console.log(result);
// output: 1*2*3*4*5

32 splice()

  • The splice() method can:
    • remove elements from the array
    • replace elements in the array
    • add elements in the array
  • It mutates the original array.
  • It accepts the following arguments:
    • start - index from which to mutate the array
    • deleteCount - the number of elements to delete, 0 or negative if no elements to delete
    • item1, item2, … itemn - items to add to the array

Test cases:

  1. start is given; deleteCount and items not given
    • deletes all items from the start index
  2. negative start is given; deleteCount and items not given
    • start is counted backward from the end of the array and all items starting from it are deleted
  3. start is given and deleteCount is given; deleteCount is greater than 0; items not given
    • it deletes deleteCount number of elements starting from the start index in the array
  4. start, deleteCount, items are given; deleteCount is 0
    • the items are added to the array from the start index
  5. start, deleteCount, items are given; deleteCount is greater than 0
    • it deletes deleteCount number of elements starting from the start index in the array
    • it then adds the items in the array from the start index

Custom implementation:

Array.prototype.mySplice = function () {
  let returnResult = [];
  let start, deleteCount;
  let items = [];

  if (arguments.length === 0) {
    return returnResult;
  }

  // start
  start = arguments[0];
  if (start >= this.length) {
    return returnResult;
  }
  if (start < 0) {
    start = this.length + start;
  }

  // deleteCount
  if (arguments.length === 1) {
    deleteCount = this.length - start;
  }
  if (arguments.length >= 2) {
    deleteCount = arguments[1];

    if (deleteCount > this.length - start) {
      deleteCount = this.length - start;
    }
  }

  // items
  if (arguments.length > 2) {
    for (let i = 2; i < arguments.length; i++) {
      items.push(arguments[i]);
    }
  }

  // delete elements if delete count > 0
  if (deleteCount > 0) {
    for (let i = 0; i < deleteCount; i++) {
      returnResult.push(this[start + i]);
    }

    for (let i = start, j = 0; i <= this.length - deleteCount; i++, j++) {
      this[start + j] = this[start + j + deleteCount];
    }

    this.length = this.length - deleteCount;
  }

  // add elements if items are provided
  if (items.length > 0) {
    for (let i = this.length - 1; i >= start; i--) {
      this[i + items.length] = this[i];
    }

    for (let i = 0; i < items.length; i++) {
      this[start + i] = items[i];
    }
  }
  return returnResult;
};

Output:

// Test case 1:
const arr1 = ["a", "b", "c", "d", "e"];
const result1 = arr1.mySplice(2); 
console.log(arr1); // ['a', 'b']
console.log(result1); // ['c', 'd', 'e']

// Test case 2:
const arr2 = ["a", "b", "c", "d", "e"];
const result2 = arr2.mySplice(-2);
console.log(arr2); // ['a', 'b', 'c'] 
console.log(result2); //  ['d', 'e']

// Test case 3:
const arr3 = ["a", "b", "c", "d", "e"];
const result3 = arr3.mySplice(2, 1);
console.log(arr3); // ['a', 'b', 'd', 'e']
console.log(result3); // ['c']

// Test case 4:
const arr4 = ["a", "b", "c", "d", "e"];
const result4 = arr4.mySplice(2, 0, "A", "B");
console.log(arr4); 
// ['a', 'b', 'A', 'B', 'c', 'd', 'e']
console.log(result4); // []

// Test case 5:
const arr5 = ["a", "b", "c", "d", "e"];
const result5 = arr5.mySplice(2, 1, "A");
console.log(arr5);
// ['a', 'b', 'A', 'd', 'e']
console.log(result5); 
// ['c']

Contributing 👩‍💻

You can find the code in the following repository 👇

There might be some improvements that can be made or some test cases that I’ve missed addressing. You can create an issue in this repository if you find any improvement that can be made. Thanks!

That’s all folks 👋

These were the custom implementations of array methods I worked on. Let's connect: