DEV Community

Cover image for Optimizing Loop In JavaScript
SR Sajjad
SR Sajjad

Posted on • Updated on

Optimizing Loop In JavaScript

If you've been writing code for a while you might've developed a level of empathy for your machine. "It shouldn't work much while running my code". Not speaking of hard core algorithm optimization. But yah, it surely feels bad when there's a loop inside another loop.

While writing JavaScript, there are many places we could optimize our code to run faster.
Like -

  • take the hot code out of main thread
  • make async calls parallel with Promise.all
  • debounce or throttle certain functions
  • use CSS properties that will trigger less rendering cycles

...and such points go on.

One most obvious thing among them is Loop. Loops are scary. Whenever I start writing a loop, the "Perf Demon" appears and starts talking shit to my head. Some good points to note though.

But maybe in most cases, the loop isn't the reason for bad performance but you might want to avoid unnecessary iterations. Hence the blog post people !

The target is actually any kind of repetition like - loop, recursion or whatever

In JavaScript there are many APIs to write loop. These days we mostly use map, reduce, filter, forEach. It feels really good to write them. Because they enforce functional approach and the code management is quite good as well. Some people hate reduce though ;) .

Let's jump to the points - how we can write better Loops ?

 

The Magic Words - break, continue and return

Let's look at this example -

for(let i = 0; i < arr.length; i++){

    // we have got our answer 
    break

    // we don't need to go further in this iteration
    continue

    // heavy calculation here
}
Enter fullscreen mode Exit fullscreen mode

Do you see what's happening here ? Now in forEach, map, filter - they don't stop. They are going to run through all iterations until the last index. Break doesn't work.

So in such cases we should pick for loop instead of trying to be cool. While the classic for loop is perfectly fine, you might want to use a better looking API - for of.

for (let val of arr){

    // we have got our answer 
    break

    // we don't need to go further in this iteration
    continue

    // heavy calculation here
}
Enter fullscreen mode Exit fullscreen mode

Now the gotcha is - what if we need index inside this loop ? In for of there's no direct index support. But there's always a hack for almost everything.

for (let [index, val] of Object.entries(arr)){

}
Enter fullscreen mode Exit fullscreen mode

If the loop is inside a function, and we use return inside that loop, the whole function is going to return.

function doSomething(){

  for(let val of arr){

    // mission accomplished
    return

    // some heavy calculation going on here
  }

}
Enter fullscreen mode Exit fullscreen mode

This can't be done in forEach, map or some other array looping functional method. Because they have their own return.

 

You Might Not Need Iteration

Let's look at another example -

let playerInfo = [
    {
        name: 'Messi',
        club: 'Barcelona'
    },

    {
        name: 'Ronaldo',
        club: 'Juventus'
    },

    {
        name: 'Neymar',
        club: 'PSG'
    }
]

// here we want to find Neymar's club from this array
console.log(playerInfo.find(player => player.name === 'Neymar').club)
Enter fullscreen mode Exit fullscreen mode

For this we need to loop over each element and see if it's Neymar and then get the club's value.

Sometimes a hashing/ dictionary approach would be better. Because then we don't need to iterate again and again. Just access the value directly.

const playerInfo = {
  Messi: 'Barcelona',
  Ronaldo: 'Juventus',
  Neymar: 'PSG'
}

console.log(playerInfo.Neymar)
Enter fullscreen mode Exit fullscreen mode

Maybe it's not the best example, but I am pretty sure you'd find better use case for this approach.

In some cases, this kind of object approach can save you from O(N^2) complexity.

// let's find out if there's any repetition in this array
let arr = [1, 2, 3, 1] // 1 appears twice, so there's repetition

// loop on every item
// on another inner loop check -
// if this item has appeared in any other index
// so that would be O(N^2) solution


// Or,
// hash the value on one loop
let obj = {}
arr.forEach((v,i) => obj[v] ? obj[v]++ : obj[v] = 1)

// and on another loop check if some key has more than 1 value
// that would be of O(N+N) complexity and that's better
Enter fullscreen mode Exit fullscreen mode

For some cases, you might consider a math equation instead of loop. Like - find out the summation of an explicit sequence.

let arr = [1, 2, 3, 4, 5]

// which is better in this case ?

// this ?
let total = arr.reduce((currentVal, reducedVal) => currentVal + reducedVal , 0)

// or this ?
let n = 5 // last element - arr[arr.length - 1]
let sum = (n * (n+1)) / 2


// another dumb example
// which is better here ?
let arr = [2, 2, 2, 2, 2, 2]

// this ?
let total = eval(arr.join('+')) // eval ? really ??

// or this ?
let sum = 2 * arr.length 
Enter fullscreen mode Exit fullscreen mode

 

Use the Correct Array Method for Particular Scenario

There are varieties of built in array methods available in JavaScript. Some are similar but each one has it's own purpose. It's better to think twice before applying map or filter for all use cases.

For example - find vs filter

find is a better suit if we are looking for only one item. And find stops iterating after the desired value is found. filter would iterate until the last index as it's looking for all the matches.

There are other same cases.

 

Memoization

Sometimes there could be same function calls with same parameters, in those cases we can save the value on first execution. Instead of running the function again, we could just use that saved value. This process is called memoization.

Just to give a rough idea - a silly example - look for better examples on the internet.

let cache = {}

function plus(x){
  // there might be heavy calculation here
  console.log('i am here') // LOL
  return x + 2
}

function memoizedPlus(a){
  if(cache[a]){
    return cache[a]
  }

  else{
    cache[a] = plus(a)
    return cache[a]
  }
}

// run this code in your console and see what happens​

console.log(memoizedPlus(5))
console.log(memoizedPlus(1))
console.log(memoizedPlus(5))
console.log(memoizedPlus(3))
console.log(memoizedPlus(3))
Enter fullscreen mode Exit fullscreen mode

I had a plan to talk about handling async operation inside loop. Maybe in another article. So for now that's all folks ! Stay safe and have fun.

Loop

Top comments (0)