A brief discussion on how to write beautiful conditional expressions in JS

A brief discussion on how to write beautiful conditional expressions in JS

Multiple conditional statements

Multiple conditional statements using Array.includes

For example

function printAnimals(animal) {
  if (animal === "dog" || animal === "cat") {
    console.log(`I have a ${animal}`);
  }
}

console.log(printAnimals("dog")); // I have a dog

This way of writing seems to be fine when there are fewer conditions. At this time, we only have 2 kinds of animals, but what if we have more conditions to judge (more animals)? If we continue to expand the judgment conditions, the code will become difficult to maintain and the logic will be unclear.

Workaround

You can use Array.includes to rewrite conditional statements

function printAnimals(animal) {
  const animals = ["dog", "cat", "hamster", "turtle"];

  if (animals.includes(animal)) {
    console.log(`I have a ${animal}`);
  }
}

console.log(printAnimals("hamster")); // I have a hamster

Here, we create an array of animals in order to extract the conditional separately from the rest of the code. Now, if we want to check for any other animals, all we need to do is add a new array item.

We can also use the animals variable outside the scope of this function to reuse it elsewhere in the code. This is a way to write code that is clearer, easier to understand, and maintain. Isn't it?

Multi-attribute object

This is a very good trick to condense your code and make it look cleaner. Let's take the previous example and add a few more conditions. What if the animal is not a simple string, but an object with some properties?

So now the requirement is:

  • If there is no animal, throw an error
  • Print the type of animal
  • Print the name of the animal
  • Print the sex of the animal
const printAnimalDetails = (animal) => {
  let result; // declare a variable to store the final value

  // condition 1: check if animal has a value
  if (animal) {
    // condition 2: check if animal has a type property
    if (animal.type) {
      // condition 3: check if animal has a name property
      if (animal.name) {
        // condition 4: check if animal has a gender property
        if (animal.gender) {
          result = `${animal.name} is a ${animal.gender} ${animal.type};`;
        } else {
          result = "No animal gender";
        }
      } else {
        result = "No animal name";
      }
    } else {
      result = "No animal type";
    }
  } else {
    result = "No animal";
  }

  return result;
};

console.log(printAnimalDetails()); // 'No animal'

console.log(printAnimalDetails({ type: "dog", gender: "female" })); // 'No animal name'

console.log(printAnimalDetails({ type: "dog", name: "Lucy" })); // 'No animal gender'

console.log(
  printAnimalDetails({ type: "dog", name: "Lucy", gender: "female" })
); // 'Lucy is a female dog'

The above code works fine, but the code is long and hard to maintain. Without using the tooltip, you might waste some time trying to determine where the closing bracket is. Imagine what would happen if the code had more complex logic. Lots of if...else statements!

We can refactor the above function using ternary operator , && conditional, etc., but let’s write a more precise code using multiple return statements.

const printAnimalDetails = ({ type, name, gender } = {}) => {
  if (!type) return "No animal type";
  if (!name) return "No animal name";
  if (!gender) return "No animal gender";

  // Now in this line of code, we're sure that we have an animal with all //the three properties here.

  return `${name} is a ${gender} ${type}`;
};

console.log(printAnimalDetails()); // 'No animal type'

console.log(printAnimalDetails({ type: dog })); // 'No animal name'

console.log(printAnimalDetails({ type: dog, gender: female })); // 'No animal name'

console.log(printAnimalDetails({ type: dog, name: "Lucy", gender: "female" })); // 'Lucy is a female dog'

In the refactored version, destructuring and default parameters are also included. Default parameters ensure that if we pass undefined as an argument to a method, we still have a value to destructure, in this case an empty object {}.

Typically, code is written between these two methods.

For example

function printVegetablesWithQuantity(vegetable, quantity) {
  const vegetables = ["potato", "cabbage", "cauliflower", "asparagus"];

  // condition 1: vegetable should be present
  if (vegetable) {
    // condition 2: must be one of the item from the list
    if (vegetables.includes(vegetable)) {
      console.log(`I like ${vegetable}`);

      // condition 3: must be large quantity
      if (quantity >= 10) {
        console.log("I have bought a large quantity");
      }
    }
  } else {
    throw new Error("No vegetable from the list!");
  }
}

printVegetablesWithQuantity(null); // No vegetable from the list!
printVegetablesWithQuantity("cabbage"); // I like cabbage
printVegetablesWithQuantity("cabbage", 20);
// 'I like cabbage`
// 'I have bought a large quantity'

Now, we have:

  • If/else statements to filter invalid conditions
  • 3 levels of nested if statements (conditions 1, 2, and 3)
  • A general rule is to return early when an invalid condition is found.
function printVegetablesWithQuantity(vegetable, quantity) {
  const vegetables = ["potato", "cabbage", "cauliflower", "asparagus"];

  // condition 1: throw error early
  if (!vegetable) throw new Error("No vegetable from the list!");

  // condition 2: must be in the list
  if (vegetables.includes(vegetable)) {
    console.log(`I like ${vegetable}`);

    // condition 3: must be a large quantity
    if (quantity >= 10) {
      console.log("I have bought a large quantity");
    }
  }
}

By doing this, we reduce one level of nested statements. This coding style is good, especially when using long if statements. We can further reduce the nested if by inverting the condition and returning early.

Please see below how condition 2 is done:

function printVegetablesWithQuantity(vegetable, quantity) {
  const vegetables = ["potato", "cabbage", "cauliflower", "asparagus"];

  if (!vegetable) throw new Error("No vegetable from the list!");
  // condition 1: throw error early

  if (!vegetables.includes(vegetable)) return;
  // condition 2: return from the function is the vegetable is not in
  // the list

  console.log(`I like ${vegetable}`);

  // condition 3: must be a large quantity
  if (quantity >= 10) {
    console.log("I have bought a large quantity");
  }
}

By reversing the conditions of condition 2, the code no longer has nested statements. This technique is useful when we have many conditions and want to stop further processing if any particular condition is not satisfied.

So always aim to reduce nesting and return early, but don't overdo it.

Replace the Switch Statement

Let's look at the following example, where we want to print fruits according to their color:

function printFruits(color) {
  // use switch case to find fruits by color
  switch (color) {
    case "red":
      return ["apple", "strawberry"];
    case "yellow":
      return ["banana", "pineapple"];
    case "purple":
      return ["grape", "plum"];
    default:
      return [];
  }
}

printFruits(null); // []
printFruits("yellow"); // ['banana', 'pineapple']

The above code is correct, but it is very verbose. The same result can be achieved using a more concise syntax.

// use object literal to find fruits by color
const fruitColor = {
  red: ["apple", "strawberry"],
  yellow: ["banana", "pineapple"],
  purple: ["grape", "plum"],
};

function printFruits(color) {
  return fruitColor[color] || [];
}

Similarly, you can also use Map to achieve:

// use Map to find fruits by color
const fruitColor = new Map()
  .set("red", ["apple", "strawberry"])
  .set("yellow", ["banana", "pineapple"])
  .set("purple", ["grape", "plum"]);

function printFruits(color) {
  return fruitColor.get(color) || [];
}

Map is an object type available since ES5, which allows key-value storage.

For the example above, you can use Array.filter to achieve the same result.

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 printFruits(color) {
  return fruits.filter((fruit) => fruit.color === color);
}

Default parameters and destructuring

When working with JavaScript, we always need to check for null/undefined and assign a default value or the compilation breaks.

function printVegetablesWithQuantity(vegetable, quantity = 1) {
// if quantity has no value, assign 1

  if (!vegetable) return;
    console.log(`We have ${quantity} ${vegetable}!`);
  }
  //results
}

printVegetablesWithQuantity('cabbage'); // We have 1 cabbage!
printVegetablesWithQuantity('potato', 2); // We have 2 potato!

What if Vegetable is an object? Can we assign a default parameter?

function printVegetableName(vegetable) {
  if (vegetable && vegetable.name) {
    console.log(vegetable.name);
  } else {
    console.log("unknown");
  }
}

printVegetableName(undefined); // unknown
printVegetableName({}); // unknown
printVegetableName({ name: "cabbage", quantity: 2 }); // cabbage

In the above example, we want to print the name of the vegetable if it is available or print unknown.

We can avoid the conditional if (vegetable && vegetable.name){} by using default parameters & destructuring.

// destructing - get name property only
// assign default empty object {}

function printVegetableName({ name } = {}) {
  console.log(name || "unknown");
}

printVegetableName(undefined); // unknown
printVegetableName({}); // unknown
printVegetableName({ name: "cabbage", quantity: 2 }); // cabbage

Since we only need the property name, we can change the structure of the parameter to use {name}, and then we can use name as a variable in the code instead of using vegetable.name.

We have also assigned an empty object {} as the default value, otherwise when we execute printVegetableName(undefined), it will give an error – Cannot destructure property name of undefined or null, because there is no name property in undefined.

Match all or part of the conditions

We can reduce the number of lines of code by using these Array methods.

In the following code, we want to check if all fruits are red:

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 above code is too long, we can reduce the number of lines of code by using 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
}

Similarly, if we want to test whether any fruit is red, we can use Array.some:

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
}

Using optional chaining and Nullish merging

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/%E5%8F%AF%E9%80%89%E9%93%BE

These two features are very useful for writing more concise conditionals in JavaScript. At the time of writing, they are not fully supported and may need to be transpiled with Babel .

Optional chaining is able to handle tree-like structures without the need to explicitly check if intermediate nodes exist, and Nullish works very effectively in conjunction with optional chaining to ensure that default values ​​for non-existent nodes are present.

For example:

const car = {
  model: "Fiesta",
  manufacturer:
    name: "Ford",
    address:
      street: "Some Street Name",
      number: "5555",
      state: "USA",
    },
  },
};

// to get the car model
const model = (car && car.model) || "default model";

// to get the manufacturer street
const street =
  (car &&
    car.manufacturer &&
    car.manufacturer.address &&
    car.manufacturer.address.street) ||
  "default street";

// request an un-existing property
const phoneNumber =
  car &&
  car.manufacturer &&
  car.manufacturer.address &&
  car.manufacturer.phoneNumber;

console.log(model); // 'Fiesta'
console.log(street); // 'Some Street Name'
console.log(phoneNumber); // undefined

So if we want to print out if the car manufacturer is from the United States, the code would look like this:

const isManufacturerFromUSA = () => {
  if (
    car &&
    car.manufacturer &&
    car.manufacturer.address &&
    car.manufacturer.address.state === "USA"
  ) {
    console.log("true");
  }
};

checkCarManufacturerState(); // 'true'

You can clearly see how messy this can become for more complex object structures. There are some third-party libraries like lodash or idx that have their own functionality. For example lodash has a _.get method. However, this feature was introduced in the JavaScript language itself.

Here’s how these new features work:

// to get the car model
const model = car?.model ?? "default model";

// to get the manufacturer street
const street = car?.manufacturer?.address?.street ?? "default street";

// to check if the car manufacturer is from the USA
const isManufacturerFromUSA = () => {
  if (car?.manufacturer?.address?.state === "USA") {
    console.log("true");
  }
};

Currently in Stage 3.

The above is some sharing of conditional expressions based on JavaScript. I hope it can be helpful to you.

The above is a brief discussion of how to write beautiful conditional expressions in JS. For more information on how to write beautiful conditional expressions in JS, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Summary of optimization techniques for conditional statements in JavaScript
  • How to write better conditional statements in JavaScript
  • JSP implements general paging component with query conditions
  • Detailed explanation of 5 rules for writing good JS conditional statements
  • Share 5 tips to help you write better JavaScript conditional statements
  • Detailed explanation of the use of conditional statements in JavaScript
  • Detailed explanation of conditional comments in JScript
  • Javascript Basic Tutorial - if Conditional Statement
  • Conditional judgment in javascript

<<:  MySQL 5.7 deployment and remote access configuration under Linux

>>:  Implementation of modifying configuration files in Docker container

Recommend

Solution to the problem of z-index not taking effect in CSS3

I recently wrote a combination of CSS3 and js, an...

Solution to MySQL service 1067 error: modify the mysql executable file path

Today I encountered the MySQL service 1067 error ...

Implementation of Docker CPU Limit

1. --cpu=<value> 1) Specify how much availa...

Example analysis of the principle and solution of MySQL sliding order problem

This article uses examples to explain the princip...

A brief discussion on the application of Html web page table structured markup

Before talking about the structural markup of web...

Explanation of the process of docker packaging node project

As a backend programmer, sometimes I have to tink...

The difference between Vue interpolation expression and v-text directive

Table of contents 1. Use plugin expressions 2. Us...

How to hide and remove scroll bars in HTML

1. HTML tags with attributes XML/HTML CodeCopy co...

Get the calculated style in the CSS element (after cascading/final style)

To obtain the calculated style in a CSS element (t...

Oracle VM VirtualBox installation of CentOS7 operating system tutorial diagram

Table of contents Installation Steps Environment ...

JavaScript quickly implements calendar effects

This article example shares the specific code of ...

How to create a project with WeChat Mini Program using typescript

Create a project Create a project in WeChat Devel...

JS version of the picture magnifying glass effect

This article shares the specific code of JS to ac...

The easiest way to install MySQL 5.7.20 using yum in CentOS 7

The default database of CentOS7 is mariadb, but m...