Implementing custom functions in Arrays
Tags: javascript, webdev, programming
Published: 2023-01-10 08:13:58

There are quite a lot of handy functions in Javascript arrays that developers can easily use. Some of these are higher-order-functions like map, reduce and filter. Apart from these there are lot of other functions too like flatten, splice etc. We would be looking into re-creating some of the functions out of scratch.

1. **Flatten**

Flatten method takes a depth as parameter and converts an array containing arrays into an array of plain primitives. 

e.g ``` let arr = [1,2,[3,4],5];
        arr.flat(1); // outputs [1,2,3,4,5] ```

We can pass any numeric value to flatten the Array data-structure to the level of depths. Let's try to mock this function and create our own custom implementation.

First let's go ahead and create a function called **_myFlatFn_** that takes an array. Initially we will try to implement the function in such a way that it doen't take any flattening parameter. We will try to return the entire array flattened into a single array.

For this, we need to think of a recursive approach. We will take a _for_ loop for looping through all the elements. We need to check if at any index, the value is an array or not. If it's an array, we will pass the array at that index into our custom _myFlatFn_. We then return the array from the function.

After this the callstack moves back to the original index + 1 and we continue to repeat the above steps till we have traversed the entire array. In the final step we return our result. _Code is written below_

```
Array.prototype.myFlatFn = function() { 
   let res = [];
   if( depth === undefined ) {
      for( let i = 0 ; i < this.length; i++ ) {
         if( !Array.isArray(this[i]) ) res.push(this[i]);
         else{
            let flattened = this[i].myFlatFn();
            res = [...res, ...flattened ]
         }
      }
   }

return res
}
```

So, we have got the custom implementation for custom flat without a depth value. Now , let us try to implement the implementation when we have a **_depth parameter_** passed. 

What change do we need to make to our previous implementation here? If we observe closely, we can see that the depth value will flatten the array to a point where the depth of any index of the original array is  > 0 . Let's try to grab out heads around this.

Suppose , we have an array ```[1,2,[3,4,[5,[6, [7,8]]]]]``` and we pass a depth of let's say 2. So , we continue to flatten our array till we have a flattened structure <= 2 . it means, if we check the ...[5,6,[7,8]]] value, it is at a depth of 4. So, we need to keep flattening it to depth <= 2. Max depth will be 2. We will still be left off with a depth of 4-2 = 2. Hence , we can't flatten the ...[5,6,[7,8]]] beyond ...[6,[7,8]]. Let's try to implement the code now.

```
Array.prototype.myFlatFn = function(depth) { 
    let res = [];
    if( depth ===undefined ) {
      for( let i = 0 ; i < this.length; i++ ) {
        if( !Array.isArray(this[i]) ) res.push(this[i]);
        else{
          let flattened = this[i].myFlatFn();
          res = [...res, ...flattened ]
        }
      }
    }else{
        for( let i = 0 ; i < this.length; i++ ) {
        if( !Array.isArray(this[i]) ) res.push(this[i]);
        else{
           if( depth > 0 && depth ) { 
              let flattened = this[i].myFlatFn(depth-1);
              res = [...res, ...flattened ]
           }else{
              res.push(this[i])
           }
         }
      }
   }
   return res
}
```

The key here is the recursion which we need to handle based on whether the array element at index **i** is an array or not.

Let's finally refactor our code to give a more concise look.

```
Array.prototype.myFlatFn = function(param) { 
   let res = [], arr = this ; 
   for( let i = 0 ; i < arr.length; i++ ) {
      if( param === undefined ) {
         if( !Array.isArray(arr[i]) ) res.push(arr[i])
         else{
            let flattened = arr[i].myFlatFn();
            res = [...res, ...flattened ]
         }
      }else if( param >= 0 && param !== undefined) { 
         if( !Array.isArray(arr[i]) ) res.push(arr[i])
         else {
           let flattened = arr[i].myFlatFn(param-1);
           res = [...res, ...flattened ]
      }
    }
    else res.push(arr[i])
  }
  return res
}
```

So, we have successfully implemented our own flatten function. This can be extended to any array in Javascript

2. **Map** 

Another useful utility function in Javascript is the map function. Along with reduce and filter, the map functions is a convenient way to perform some operation on each element of a given array. We are going to discuss each of these functions in details and try to replicate the same with our custom implementation.

First of all what does map function do. Well, it takes a function and an array and returns a modified array with each element in the array converted based on the function provided. Let's see an example

``` 
let arr = [1,2,3,4,5] ;
arr.map( el => el*el ) // Outputs the squares of each element .i.e. [ 1,4,9, 25]
```

The result is straightforward. It squares each element in the array. How can we achieve this?

We need to initialize an array, let's just say **_result_** and then keep pushing each element from the original array to the result array after the operation. We will try to understand this with code.

``` 
Array.prototype.customMap = function( fn ) {
   let result = [];
   for( let i of this ) 
      result.push( fn(i) )
      return result;
}
```

So, what have we done? We are just taking each element in the array, taking the transformer function(**fn**) and applying it to each of them before pushing it to the result array. Finally , we are just returning the result. Thus we have completed our custom map function too. Let's move to the filter function.

3. **Filter**

Just like map, filter also returns a modified array based on a condition being passed as a parameter and filters out all the elements that don't pass the specific condition. Let's see an example.

``` 
let arr = [1,2,3,4,5,6,7] ;
arr.filter( el => el > 3 ); // returns a new array 
[4,5,6,7] in this case
```.

So, how can we implement this. Simple. We need to have the same "**result**" array and keep pushing any element that satisfies the passed condition before finally returning the result array. Let's see this with the help of code

```
Array.prototype.customFilter = function(condition){
   let result = [];
   for( let i of this )
      if( condition(i) ) result.push(i)
   return result
}
```

We have now successfully implemented a custom filter function in Javascript.