# 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1662827454851/BXlRRsGmo.png align="left")



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](https://github.com/siddhigate/js-polyfills) 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:**

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

**Output:**

```jsx
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:**

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

**Output:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

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

**Output:**

```jsx
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:**

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

**Output:**

```jsx
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:**

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

**Output:**

```jsx
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:**

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

**Output:**

```jsx
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:**

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

**Output:**

```jsx
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:**

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

**Output:**

```jsx
// 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:**

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

**Output:**

```jsx
// 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:**

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

**Output:**

```jsx
// 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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

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

**Output:**

```jsx
// 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:**

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

**Output:**

```jsx
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:**

```jsx
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:**

```jsx
// 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:**

```jsx
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:**

```jsx
// 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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
// 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:**

```jsx
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:**

```jsx
// 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:**

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

  function* iterator() {
    yield* keys;
  }

  return iterator();
};
```

**Output:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
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:**

```jsx
// 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 👇

%[https://github.com/siddhigate/js-polyfills]

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:

- [Twitter](https://twitter.com/SiddhiGate)
- [Linkedin](https://www.linkedin.com/in/siddhigate/)
- [GitHub](https://github.com/siddhigate)
