3

Suppose I have a snippet like this,

let arr = [{...},{...} ...]// huge array of objects;

arr.forEach(i => {
  if(someOtherProperty == "one") {
    ...
  } else if(someOtherProperty == "two") {
    ...
  }...

})

Basically, I have an if-else-if ladder inside a loop.

The condition does not depend on the array item.

I want to know if it is possible to evaluate the if condition prior to execution of the loop since the condition is static/constant throughout the loop run

Few ways I can think of is

  1. keep the loop inside each of the if/else blocks. This way the if condition will be executed only once but I have more codes.
  2. Use an object like

    let condition = {
      one: someOtherProperty == "one",
      two: someOtherProperty == "two",
      ...
    }
    

    And use it in the condition like if(condition.one) and so on.

    Please suggest a better way to deal with situations like this to improve the efficiency.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
Satish Kumar
  • 601
  • 6
  • 14
  • Can you explain what you're trying to achieve? – Phiter Mar 27 '18 at 19:40
  • @Phiter, It feels like the code is inefficient. Since the size of array is huge and there are quite a few else-if blocks. For each iteration, conditions are evaluated which I think can be done prior loop run but don't know the best way. – Satish Kumar Mar 27 '18 at 19:44
  • Would a `switch` statement work here? I'm not quite sure I understand the problem. – jhpratt Mar 27 '18 at 19:45
  • @jhpratt See my above comment, I guess I have made it more clear. – Satish Kumar Mar 27 '18 at 19:55
  • 1
    [branch prediction](https://stackoverflow.com/a/11227902/1541563) may or may not influence the speed of the `if` statement if consistent results are expected, so pre-optimizing may not be necessary at all. Your biggest slowdown will be the use of `forEach()` instead of `for` statement due to context-switching overhead. – Patrick Roberts Mar 27 '18 at 20:07
  • @PatrickRoberts. In this case, as the condition is same for all the iterations, I guess [this](https://stackoverflow.com/questions/49521159/is-there-a-way-to-make-condition-of-if-static-constant/49521270#49521270) won't improve performance. – Satish Kumar Mar 27 '18 at 20:27
  • @SatishKumar it should, though the sacrifice in "elegance" of the optimization may not be worth it since the performance improvement would be very minor, if it's even noticeable at all. If in doubt, always run benchmarks on as many browsers as you can. – Patrick Roberts Mar 27 '18 at 20:44

4 Answers4

2

I think that in this case there is a huge difference between the code inside the conditionals, you can make your code look better using the following approach:

let arr = [1, 2, 3, 4, 5];
let str = "one";
const conditions = {
  "one": function(item, index) {
    console.log("one", item);
  },
  "two": function(item, index) {
    console.log("two", item);
  }
}

arr.forEach(conditions[str]);

Or if you want to stick to the basics, you can do this, too:

let arr = [1, 2, 3, 4, 5];
let str = "one";
var one = function(item, index) {
  console.log("one", item);
};
var two = function(item, index) {
  console.log("two", item);
};
switch (str) {
  case "one":
    arr.forEach(one);
    break;
  case "two":
    arr.forEach(two);
    break;
}
Phiter
  • 14,570
  • 14
  • 50
  • 84
0

To save code duplication, you can perform the conditional check once, iterate through your loop, then put the rest of your code in functions.

You could also place your loop iteration code in a function and call that, depending on what you are doing in the loop.

let arr = [{...},{...} ...]// huge array of objects;

if(someOtherProperty == "one") {
    arr.forEach(i => { ...functionOnArray(); });
    functionOne();
} else if(someOtherProperty == "two") {
    arr.forEach(i => { ...functionOnArray(); });
    functionTwo();
}...

function functionOnArray(){ ... }
function functionOne(){ ... }
function functionTwo(){ ... }
Steve Padmore
  • 1,710
  • 1
  • 12
  • 18
0

The best solution that comes to mind is using a functional approach for that.

let arr = [1,2,3,4,5,6,7,8,9]; // Huge array

let condFactory = (condition) => {
    if (condition === 'one') {
        console.log('This will only be called once, if condition==="one"',
                    'it will not be called for each element')
        return (item, index) => console.log('one (called for each element)', item, index);
    } else if (condition === 'two') {
        console.log('This will only be called once, if condition==="two"',
                    'it will not be called for each element')
        return (item, index) => console.log('two (called for each element)', item, index);
    }

    console.log('This will only be called once, if no condition matches',
                'it will not be called for each element')
    return (item, index) => console.log('No condition matched (called for each element)', item, index);
}

arr.forEach(condFactory(someOtherProperty));

For es5:

var arr = [1,2,3,4,5,6,7,8,9]; // Huge array

var condFactory = function(condition) {
    if (condition === 'one') {
        console.log('This will only be called once, if condition==="one"',
                    'it will not be called for each element')
        return function(item, index) { console.log('one (called for each element)', item, index); };
    } else if (condition === 'two') {
        console.log('This will only be called once, if condition==="two"',
                    'it will not be called for each element')
        return function(item, index) { console.log('two (called for each element)', item, index); };
    }

    console.log('This will only be called once, if no condition matches',
                'it will not be called for each element')
    return function(item, index) { console.log('No condition matched', item, index); };
}

arr.forEach(condFactory(someOtherProperty));

This way the function to be used for each condition will be determined before the loop starts and used for every element inside the loop.

You can stretch this oneliner

arr.forEach(condFactory(someOtherProperty));

into two lines to see why the if...else is only evaluated once:

let functionBasedOnIfElse = condFactory(someOtherProperty); // Get the function based on your "someOtherProperty"
arr.forEach(functionBaseOnIfElse); // Pass reference to the function

As you can see, the if...else will only be evaluated once. The returned function on that if...else will then be passed to arr.forEach and called for each element inside the array.

Edit: Added default return.

Edit2: Corrected es5 snipped

Edit3: Expands cases to show that each function is applied to each item inside the array.

Edit4: Adds more information on why the if...else is only evaluated once.

Edit5: Adds a few console.log to show which part will be called at which point.

pascalpuetz
  • 5,238
  • 1
  • 13
  • 26
  • How is your snippet/method any different than one provided in question? – Satish Kumar Mar 29 '18 at 17:41
  • `condFactory(someOtherProperty)` returns a function based on the condition. Expanding the example: `let determinedFunction = condFactory(someOtherProperty); // The function based on the condition` `arr.forEach(determinedFunction) // pass the function reference` This way, the if...else will be determined only once -> based on that a function is returned -> this function will be called for each element inside the array. I'll update the answer to include this. – pascalpuetz Mar 29 '18 at 18:18
  • @SatishKumar I updated my answer to show how it is different to the approach in the question. – pascalpuetz Mar 29 '18 at 18:25
  • I believe that you are wrong about "This way, the if...else will be determined only once" part. You have just passed the function and it will be called for each element in the array. So conditions will be evaluated _each time_ . Also the snippet in the question and that of yours equivalent. What you did is just put the body of loop inside a function. – Satish Kumar Mar 29 '18 at 21:12
  • That's not true. The `condFactory()` function is only called once. It then returns the function depending on the condition **only once**. That function will indeed be called once for each element, but it contains only the code you want to run for that condition, not the condition itself. I'll update the answer once more to make that clear. – pascalpuetz Mar 29 '18 at 21:35
  • Aah. You are right, I apologize. I didn't notice that you were returning a function on the callback. I guess your method is almost the same as [this](https://stackoverflow.com/a/49521270/6481320). – Satish Kumar Mar 30 '18 at 07:56
-2

One thing you can do is to create a branching non-anonymous function that you map onto the array. E.g.

f =  {(x => if (prop1 == "one") 
     { ...} 
     else if (prop2 == "two") 
     {...} )}

and then do arr.forEach(i=> f(i)). This is exactly what the functional features of javascript were implemented for.

This is roughly as efficient as what you were trying to do. It will get quicker if you try to be clever about partial application and pass the conditions as an argument to that function.