DEV Community

Cover image for The problem with Higher Order Array Functions (js)
kamaaaal
kamaaaal

Posted on

The problem with Higher Order Array Functions (js)

So What are higher order functions ?

Higher order functions are functions which accepts an argument as a function or a function which returns another function. In JS we use lot of HOF(Higher Order function) and functional programming patterns. some examples

I'm using use the term functions interchangeably with methods

Function accepting callback function
Array.map, Array.filter, Array.reduce

Function returning function
Function.apply, Function.call, Function.bind

Use cases

Functional programming patterns provide easier expressive way to write code. Using Array methods helps us a lot by not writing a for loop statements by ourselves.

Array Methods

const nums = [2,4,6];

// map
const squred = nums.map( (num) => num**2);

// filter (only even numbers)
const evenNums = nums.map ( num => num%2 == 0);

Enter fullscreen mode Exit fullscreen mode

For loops

const nums = [2, 4, 6];
const squared = [];
const evenNums = [];

// map
for (let i = 0; i < nums.length; i++) {
  squared.push(nums[i] ** 2);
}

// filter (only even numbers)
for (let i = 0; i < nums.length; i++) {
  if (nums[i] % 2 === 0) {
    evenNums.push(nums[i]);
  }
}
Enter fullscreen mode Exit fullscreen mode

Array Methods provide a easier way to create a new object with .map method and with filter we can create a new array with desired elements

Array methods returns themselves(Array object), Allowing us to chain them with multiple methods.

const nums = [2,4,6,10];
// square all numbers and then sum them
const result = nums.map( (num) => num**2 ).reduce((prev,curr) => prev+curr,0)
Enter fullscreen mode Exit fullscreen mode

In the above code sample combining array methods and arrow function allows us to perform lot of operations in single line.

Obviously, it is more expressive and readable than traditional loops.

So What are my Concerns

My concerns raised whenever I'm chaining array methods. Chaining Array methods looks good to our eyes but not to the JS Garbage collector.

Let's tackle a simple problem. We have an array of numbers representing people's incomes. Our job is to calculate a 5% tax on incomes that are above 500 and then create a new array containing those incomes along with their corresponding taxes only.


const incomes = [200,300,600,800];

const output = incomes.filter((income) => income > 500).map((income) => (income * 5 / 100) + income);

console.log(output); // [ 630, 840 ]
Enter fullscreen mode Exit fullscreen mode

The above is the straightforward implementation. but we miss out the fact that to get the desired output we loop through the whole array twice and an intermediate array is also created.

Image of output arary

So algorithmically speaking as we loop through the array twice the algorithmic complexity is O(2*n) and That filtered array(intermediate) array has to be garbage collected by the v8.

What can be done

Chaining Array methods with smaller arrays will not cause much of performance impact. but when doing those with larger arrays we can write traditional loops instead of array methods, so that we don't have to loop through multiple arrays.

const incomes = [200,300,600,800];
const result = []
for (let i = 0; i < incomes.length; i ++){
    if (incomes[i] > 500){
        const withTax = incomes[i] + (incomes[i] * 5 /100)
        result.push(withTax);
    }
}
console.log(result); //[ 630, 840 ]
Enter fullscreen mode Exit fullscreen mode

in the above we are processing only the incomes above 500 and just pushing them to result array. Of course, this looks little longer than the array methods implementation.

we can use for(of) loops or .foreach() to make it little nicer.

So is this the perfect implementation ?

This approach appears to be a good one. However, there is a potential issue with this code in the following line:

        result.push(withTax);
Enter fullscreen mode Exit fullscreen mode

The .push method is used to add elements to an array, but internally, the JavaScript engine needs to make memory allocation calls to the operating system to allocate memory for the array. Making multiple operating system calls can be an expensive task, and this is abstracted by the .push method.

So, this should hold true for the .map implementation. But it's not the same. When we call the .map method on an array and the output array is going to end up with the same size, the V8 engine actually makes just one memory allocation call for the entire array.

Conclusion

so which is the faster way, It's always better to use HOF's if we are not chaining multiple methods. but I can't answer whether chaining multiple array methods or dynamically pushing elements into an array is faster. that might require multiple benchmarks and analysis.

I apologize if I made any grammatical mistakes.

Top comments (2)

Collapse
 
noob_jeffrey profile image
JEFF

eye opening

Collapse
 
urielsouza29 profile image
Uriel dos Santos Souza

Pipe/compose

pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);

compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);

Enter fullscreen mode Exit fullscreen mode