Does the order of the comparison conditions matter when using OR operators in an IF function?

Viewed 477

I'm trying to get a better understanding of the conditions within an IF statement. When I change the order of the conditions I receive a TypeError of undefined.

I receive a TypeError: Cannot read property 'length' of undefined when the order is changed to:

if (col === maze[row].length || row < 0 || col < 0 || row === maze.length) {
    return
}

Does the order of the comparisons matter when using OR operators in an IF function? What is causing the TypeError when the order is written differently?

Working code base:

const maze = [
  [' ', ' ', ' ', '*', ' ', ' ', ' '],
  ['*', '*', ' ', '*', ' ', '*', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', '*', '*', '*', '*', '*', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', 'e'],
];

const solve = (maze, row = 0, col = 0, path = "") => {

  if (row < 0 || col < 0 || row === maze.length || col === maze[row].length) {
    return
  }

  // Base case
  if (maze[row][col] === "e") {
    return console.log(`Solved at (${row}, ${col})! Path to exit: ${path}`)

    // General case
  } else if (maze[row][col] === "*") {
    return
  }

  // Marker
  maze[row][col] = "*"

  // Right
  solve(maze, row, col + 1, path.concat("R"))

  // Down
  solve(maze, row + 1, col, path.concat("D"))

  // Left
  solve(maze, row, col - 1, path.concat("L"))

  // Up
  solve(maze, row - 1, col, path.concat("U"))

  // Remove marker
  maze[row][col] = " "
}

console.log(solve(maze));
2 Answers

Does the order of the comparisons matter when using OR operators in IF statements?

Yes, in addition to operator precedence, you also need to look at associativity and short-circuit evaluation. The || operator has left-to-right associativity, meaning that it will evaluate the expressions from left-to-right. Short-circuit evaluation means that further logical conditions are ignored once the result is already known.

What is causing the TypeError when the order is written differently?

Looking at your condition:

col === maze[row].length || row < 0 || col < 0 || row === maze.length

Because the logical operations are evaluated from left-to-right, the first one to be evaluated is col === maze[row].length. When row === maze.length, then col === maze[row].length evaluates to col === undefined.length, which of course produces an error.

To fix this, you need to run this condition after first confirming that the index won't be out-of-bounds. A simple way to do this is:

row < 0 || col < 0 || row === maze.length || col === maze[row].length

Now, if any of the first three conditions is true, then JavaScript doesn't bother evaluating the rest, since it already knows that the result is true. Because of this, it doesn't crash anymore.

(Keep in mind that true || false === true, so as soon as you see true || then you don't even need to read the rest of the expression to know that the result will be true.)


Note that if you were using a language that doesn't use short-circuit evaluation, then you would instead have to use multiple if statements to run your conditions in the right order:

if (row < 0 || col < 0 || row === maze.length) {
    return
}

if (col === maze[row].length) {
    return
}

I often find myself starting out writing code like this while I think through the order in which the checks need to happen, and then afterwards I simplify it down into a single expression.


Hope that helps!

You need to keep two things in mind.

  1. Javascript evaluation is from left to right.
  2. The OR || operator is short circuiting. Which means the first time it encounters a true expression it "short-circuits" i.e. bypasses all other expressions and just returns a true. This is basic boolean algebra.

In regards to your doubt about the TypeError: Cannot read property 'length' of undefined, either of maze[row] or maze is undefined. On running your snippet it turns out to be that maze[row] is the culprit here. This might be because since you do a row-1, in your code row might become negative causing maze[row] to be undefined.

If you turn the order to

if (row < 0 || col < 0 || col === maze[row].length || row === maze.length) {
    return
  }

whenever row < 0 i.e negative the OR operation short circuits all the other expressions. Hence maze[row].length is never evaluated and no undefined behaviour is encountered.

Related