Abstract:Let’s take a look at the following several common conditional expression scenarios, how to write beautifully!

This article was shared from Huawei Cloud Community “How to write beautiful conditional expressions – JavaScript implementation”, originally by Charles.

Condition expression is the problem that we can never hide in the process of coding, and it is also the most convenient thing for us to cheat the number of lines of code (doghead.jpg). But as a programmer, we also want to pursue our own “faith, expressibility and elegance”. Let’s look at the following common conditions to express the scene, how to write beautiful!

  • Multiconditional statement
  • Multi-attribute object
  • Replace Switch statement
  • Default parameters and deconstruction
  • Matches all or some of the conditions
  • Merge using optional chains and Nullish

Multiconditional statement

Use Array.Includes for multi-conditional statements

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 might seem fine with fewer conditions, where we only have 2 animals, but what if we had more conditions to judge? If we continue to stretch the criteria, the code will become unmaintainable and the logic will be unclear.

The solution

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 to extract the condition separately from the rest of the code. Now, if we want to check 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 so that we can reuse it elsewhere in the code. This is a way to write code that is cleaner, easier to understand, and easier to maintain. Isn’t it?

Multi-attribute object

This is a great trick to compress your code and make it look cleaner. Let’s use the previous example to add more conditions. What if the animal is not a simple string, but an object with some properties?

So now the requirements are:

  • If there are no animals, 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'

It works fine with the code above, but the code is long and hard to maintain. If you don’t use the prompt tool, you might waste some time trying to locate the close bracket. Imagine what would happen if the code were more complex logically. A lot of the if… Else statement!

We could refactor the above function using ternary operators, && conditions, and so on, but let’s write 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. The default parameter ensures that if we pass undefined as an argument to the method, we still have a value to deconstruct, in this case an empty object {}.

Typically, code is written in between these two approaches.

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:

  • Filter if/else statements for invalid conditions
  • Three layers of nested if statements (conditions 1, 2, and 3)
  • A general rule is to return as early as possible when an invalid condition is found.

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 so, we reduce the level of a nested statement. This coding style is good, especially when using long if statements. We can further reduce the nesting of ifs by reversing the condition and returning it earlier.

See how Condition 2 works:

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 condition of condition 2, the code no longer has a nested statement. This technique is useful when we have many conditions and want to stop further processing if any particular condition is not met.

Therefore, always aim for less nesting and early returns, but don’t overdo it.

Replace Switch statement

Let’s look at the following example where we want to print fruit by 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 implementation is error-free, but verbose, and the same result could 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 use Map to do this:

// 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 that allows key-values to be stored.

For the above example, 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 deconstruction

When using JavaScript, we always need to check for null/undefined and assign default values or compile interrupts.

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 vegetables were 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 example above, we want to print the vegetable name (if it is available) or unknown.

We can avoid the condition if (Vegetable && Vegetable. Name){} by using the default parameter & 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 attribute name, we can use {name} to change the structure of the parameter, and then we can use name as a variable in the code instead of using vegetable.name.

We also assign an empty object {} as the default, otherwise when executing printVegetableName(undefined), It will give an error — Cannot destructure property name of undefined or null, because there is no name property in undefined.

Matches all or some 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 the fruit is 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 verbose, we can reduce the number of lines 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
}

Merge using optional chains and Nullish

https://developer.mozilla.org…

These two features are useful for JavaScript to write more concise conditions. At the time of this writing, they are not fully supported and may need to be compiled using Babel.

Optional links can handle tree-like structures without the need to explicitly check for the presence of intermediate nodes, and Nullish is very effective in combination with optional links to ensure that default values for nodes do not exist.

Here’s an 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 wanted to print it out, if the car manufacturer was from the US, 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’ clearly shows how confusing this can become for more complex object structures. There are third-party libraries, such as Lodash or IDX, that have their own functions. Lodash, for example, has the _. Get method. However, introduce this feature into 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"); }};

It is currently in Stage 3.

The above is based on JavaScript to achieve some conditional expressions to share, I hope you can help ~

Click on the attention, the first time to understand Huawei cloud fresh technology ~