Post

5 Tips to Write Better Conditionals in JavaScript

Updated on September 15, 2020
    Default avatar

    By Jecelyn Yeen

    5 Tips to Write Better Conditionals in JavaScript

    While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

    When working with JavaScript, we deal a lot with conditionals, here are the 5 tips for you to write better / cleaner conditionals.

    1. Use Array.includes for Multiple Criteria

    Let’s take a look at the example below:

    // condition
    function test(fruit) {
      if (fruit == 'apple' || fruit == 'strawberry') {
        console.log('red');
      }
    }
    

    At first glance, the above example looks good. However, what if we get more red fruits, say cherry and cranberries? Are we going to extend the statement with more || ?

    We can rewrite the conditional above by using Array.includes (Array.includes)

    function test(fruit) {
      // extract conditions to array
      const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
    
      if (redFruits.includes(fruit)) {
        console.log('red');
      }
    }
    

    We extract the red fruits (conditions) to an array. By doing this, the code looks tidier.

    2. Less Nesting, Return Early

    Let’s expand the previous example to include two more conditions:

    • if no fruit provided, throw error
    • accept and print the fruit quantity if exceed 10.
    function test(fruit, quantity) {
      const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
    
      // condition 1: fruit must has value
      if (fruit) {
        // condition 2: must be red
        if (redFruits.includes(fruit)) {
          console.log('red');
    
          // condition 3: must be big quantity
          if (quantity > 10) {
            console.log('big quantity');
          }
        }
      } else {
        throw new Error('No fruit!');
      }
    }
    
    // test results
    test(null); // error: No fruits
    test('apple'); // print: red
    test('apple', 20); // print: red, big quantity
    

    Look at the code above, we have:

    • 1 if/else statement that filter out invalid condition
    • 3 levels of nested if statement (condition 1, 2 & 3)

    A general rule I personally follow is return early when invalid conditions found.

    /_ return early when invalid conditions found _/
    
    function test(fruit, quantity) {
      const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
    
      // condition 1: throw error early
      if (!fruit) throw new Error('No fruit!');
    
      // condition 2: must be red
      if (redFruits.includes(fruit)) {
        console.log('red');
    
        // condition 3: must be big quantity
        if (quantity > 10) {
          console.log('big quantity');
        }
      }
    }
    

    By doing this, we have one less level of nested statement. This coding style is good especially when you have long if statement (imagine you need to scroll to the very bottom to know there is an else statement, not cool).

    We can further reduce the nesting if, by inverting the conditions & return early. Look at condition 2 below to see how we do it:

    /_ return early when invalid conditions found _/
    
    function test(fruit, quantity) {
      const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
    
      if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early
      if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red
    
      console.log('red');
    
      // condition 3: must be big quantity
      if (quantity > 10) {
        console.log('big quantity');
      }
    }
    

    By inverting the conditions of condition 2, our code is now free of a nested statement. This technique is useful when we have long logic to go and we want to stop further process when a condition is not fulfilled.

    However, that’s no hard rule for doing this. Ask yourself, is this version (without nesting) better / more readable than the previous one (condition 2 with nested)?

    For me, I would just leave it as the previous version (condition 2 with nested). It is because:

    • the code is short and straight forward, it is clearer with nested if
    • inverting condition may incur more thinking process (increase cognitive load)

    Therefore, always aims for Less Nesting and Return Early but don’t overdo it. There is an article & StackOverflow discussion that talks further on this topic if you interested:

    3. Use Default Function Parameters and Destructuring

    I guess the code below might look familiar to you, we always need to check for null / undefined value and assign default value when working with JavaScript:

    function test(fruit, quantity) {
      if (!fruit) return;
      const q = quantity || 1; // if quantity not provided, default to one
    
      console.log(`We have ${q} ${fruit}!`);
    }
    
    //test results
    test('banana'); // We have 1 banana!
    test('apple', 2); // We have 2 apple!
    

    In fact, we can eliminate the variable q by assigning default function parameters.

    function test(fruit, quantity = 1) { // if quantity not provided, default to one
      if (!fruit) return;
      console.log(`We have ${quantity} ${fruit}!`);
    }
    
    //test results
    test('banana'); // We have 1 banana!
    test('apple', 2); // We have 2 apple!
    

    Much easier & intuitive isn’t it? Please note that each parameter can has it own default function parameter. For example, we can assign default value to fruit too: function test(fruit = 'unknown', quantity = 1).

    What if our fruit is an object? Can we assign default parameter?

    function test(fruit) { 
      // printing fruit name if value provided
      if (fruit && fruit.name)  {
        console.log (fruit.name);
      } else {
        console.log('unknown');
      }
    }
    
    //test results
    test(undefined); // unknown
    test({ }); // unknown
    test({ name: 'apple', color: 'red' }); // apple
    

    Look at the example above, we want to print the fruit name if it’s available or we will print unknown. We can avoid the conditional fruit && fruit.name checking with default function parameter & destructing.

    // destructing - get name property only
    // assign default empty object {}
    function test({name} = {}) {
      console.log (name || 'unknown');
    }
    
    //test results
    test(undefined); // unknown
    test({ }); // unknown
    test({ name: 'apple', color: 'red' }); // apple
    

    Since we only need property name from fruit, we can destructure the parameter using {name}, then we can use name as variable in our code instead of fruit.name.

    We also assign empty object {} as default value. If we do not do so, you will get error when executing the line test(undefined) - Cannot destructure property name of 'undefined' or 'null'. because there is no name property in undefined.

    If you don’t mind using 3rd party libraries, there are a few ways to cut down null checking:

    • use Lodash get function
    • use Facebook open source’s idx library (with Babeljs)

    Here is an example of using Lodash:

    // Include lodash library, you will get _
    function test(fruit) {
      console.log(__.get(fruit, 'name', 'unknown'); // get property name, if not available, assign default value 'unknown'
    }
    
    //test results
    test(undefined); // unknown
    test({ }); // unknown
    test({ name: 'apple', color: 'red' }); // apple
    

    You may run the demo code here. Besides, if you are a fan of Functional Programming (FP), you may opt to use Lodash fp, the functional version of Lodash (method changed to get or getOr).

    4. Favor Map / Object Literal than Switch Statement

    Let’s look at the example below, we want to print fruits based on color:

    function test(color) {
      // use switch case to find fruits in color
      switch (color) {
        case 'red':
          return ['apple', 'strawberry'];
        case 'yellow':
          return ['banana', 'pineapple'];
        case 'purple':
          return ['grape', 'plum'];
        default:
          return [];
      }
    }
    
    //test results
    test(null); // []
    test('yellow'); // ['banana', 'pineapple']
    

    The above code seems nothing wrong, but I find it quite verbose. The same result can be achieve with object literal with cleaner syntax:

    // use object literal to find fruits in color
      const fruitColor = {
        red: ['apple', 'strawberry'],
        yellow: ['banana', 'pineapple'],
        purple: ['grape', 'plum']
      };
    
    function test(color) {
      return fruitColor[color] || [];
    }
    

    Alternatively, you may use Map to achieve the same result:

    // use Map to find fruits in color
      const fruitColor = new Map()
        .set('red', ['apple', 'strawberry'])
        .set('yellow', ['banana', 'pineapple'])
        .set('purple', ['grape', 'plum']);
    
    function test(color) {
      return fruitColor.get(color) || [];
    }
    

    Map is the object type available since ES2015, allow you to store key value pair.

    Should we ban the usage of switch statement? Do not limit yourself to that. Personally, I use object literal whenever possible, but I wouldn’t set hard rule to block that, use whichever make sense for your scenario.

    Todd Motto has an article that dig deeper on switch statement vs object literal, you may read here.

    TL;DR; Refactor the syntax

    For the example above, we can actually refactor our code to achieve the same result with Array.filter .

    
     const fruits = [
        { name: 'apple', color: 'red' }, 
        { name: 'strawberry', color: 'red' }, 
        { name: 'banana', color: 'yellow' }, 
        { name: 'pineapple', color: 'yellow' }, 
        { name: 'grape', color: 'purple' }, 
        { name: 'plum', color: 'purple' }
    ];
    
    function test(color) {
      // use Array filter to find fruits in color
    
      return fruits.filter(f => f.color == color);
    }
    

    There’s always more than 1 way to achieve the same result. We have shown 4 with the same example. Coding is fun!

    5. Use Array.every & Array.some for All / Partial Criteria

    This last tip is more about utilizing new (but not so new) Javascript Array function to reduce the lines of code. Look at the code below, we want to check if all fruits are in red color:

    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'grape', color: 'purple' }
      ];
    
    function test() {
      let isAllRed = true;
    
      // condition: all fruits must be red
      for (let f of fruits) {
        if (!isAllRed) break;
        isAllRed = (f.color == 'red');
      }
    
      console.log(isAllRed); // false
    }
    

    The code is so long! We can reduce the number of lines with Array.every:

    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'grape', color: 'purple' }
      ];
    
    function test() {
      // condition: short way, all fruits must be red
      const isAllRed = fruits.every(f => f.color == 'red');
    
      console.log(isAllRed); // false
    }
    

    Much cleaner now right? In a similar way, if we want to test if any of the fruit is red, we can use Array.some to achieve it in one line.

    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'grape', color: 'purple' }
    ];
    
    function test() {
      // condition: if any fruit is red
      const isAnyRed = fruits.some(f => f.color == 'red');
    
      console.log(isAnyRed); // true
    }
    

    Summary

    Let’s produce more readable code together. I hope you learn something new in this article.

    That’s all. Happy coding!

    Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

    Learn more about us


    About the authors
    Default avatar
    Jecelyn Yeen

    author

    Still looking for an answer?

    Ask a questionSearch for more help

    Was this helpful?
     
    Leave a comment
    

    This textbox defaults to using Markdown to format your answer.

    You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

    Try DigitalOcean for free

    Click below to sign up and get $200 of credit to try our products over 60 days!

    Sign up

    Join the Tech Talk
    Success! Thank you! Please check your email for further details.

    Please complete your information!

    Get our biweekly newsletter

    Sign up for Infrastructure as a Newsletter.

    Hollie's Hub for Good

    Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

    Become a contributor

    Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

    Welcome to the developer cloud

    DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

    Learn more
    DigitalOcean Cloud Control Panel