if-else

Conditional statement is one of the most popular logic, not only in JavaScript but in any coding language. In real life, sometimes the condition to execute a block of code is nested and complicated. Therefore, knowing some modern Javascript syntax, instead of the big old If-else-if will help make our code clean and easier to debug.

This post is based on ideas from Scotch with some add-ons.

1/ Avoid using double negative

This one is straightforward, so I just make a quick demo of it.

const isItNotDog = () => { //implementation };

if (!isItNotDog) {

} 

However, sometimes it’s inevitable to remove double negative expression. In that case, let’s put some clarified comments for our teammates (and ourselves in the future).

2/ Array.includes for Multiple Criteria

Let’s take this example, where we need to validate a variable to match some or_ condition.

function isItDog(pet) {
  if (pet === "husky" || pet === "alaska") {
    console.log("This is Dog");
  }
}

It looks still ok with 2 breeds, but when the list extends, for example, to match either 1 in 10 breeds, it would be a nightmare to keep this syntax. In this situation, we can make use of JS arrays.

function isItDog(pet) {
  const dogBreeds = ["husky", "alaska", "bulldog", "pug", "golden-retriever"];
  
  if (dogBreeds.includes(pet)) {
    console.log("This is Dog");
  }
}

3/ Return the errors first

Let say now the function also requires to input the name of the pet. If it is a dog, and its name is Milky, then we name to print out one more sentence. We only print out of the pet value is not null

function isItDog(pet, name) {
  const dogBreeds = ["husky", "alaska", "bulldog", "pug", "golden-retriever"];

  if (pet) {
   if (dogBreeds.includes(pet)) {
    console.log("This is Dog");
    if (name === "Milky") {
     console.log("This dog name is Milky");
    }
   }
  } else {
   throw new Error("Null Value");
  }
}

In order to avoid nested if-else like this, we should always list out all of the edge cases, and putting their logic in front.

function isItDog(pet, name) {
 if (!pet) throw new Error("Null Value");

 const dogBreeds = ["husky", "alaska", "bulldog", "pug", "golden-retriever"];
 if (!dogBreeds.includes(pet)) throw new Error("Not Dog"); 

 console.log("This is Dog");

 if (name === "Milky") console.log("This dog name is Milky");
}

4/ Default arguments

Sometimes we want a function with multiple arguments that can be reused many times. But for some cases, some arguments are not valid to pass in. In this case, we can utilize default arguments to avoid errors.

function isItDog(pet, name === "Default") {
 console.log("This dog name is ", name);
}

In the case that the argument is an object, we usually write like this.

function isItDog(pet, name) {
  if (name && name.firstName)  {
    console.log (name.firstName);
  } else {
    console.log("unknown");
  }
}

We can use lodash:

function isItDog(pet) {
  console.log(__.get(pet, "firstName", "unknown");
}

Or we can use destructing assignment:

function isItDog(pet, {firstName} === {}) {
  console.log(firstName || "unknown");
}

//test results
isItDog(undefined); // unknown
isItDog({ }); // unknown
isItDog({ firstName: "Milky", age: 1 }); // Milky

5/ Use object literals instead of Switch

Switch statements sometimes make our code lengthy and repetitive. For example, this block of switch logic:

function test(legs) {
  switch (legs) {
    case "4":
      return ["cat", "dog"];
    case "2":
      return ["chicken", "duck"];
    default:
      return [];
  }
}

This can be rewritten using Object literals:

  const pets = {
    "4": ["cat", "dog"],
    "2": ["chicken", "duck"]
  };

function test(legs) {
  return pets[legs] || [];
}

6/ Array.every & Array.some for All / Partial Criteria

This is also straightforward, use .every if we want to check whether all elements of array pass condition; use .some if we want to check whether exist one element passes condition. This is to avoid writing multiple for loops.

  for (let pet of pets) {
    if (pet === "Alaska") { return "Exist Dog" }
   }
   // can be written as
   return pets.some(pet => pet === "Alaska") ? "Exist Dog" : "Not Dogs";
  for (let pet of pets) {
    if (!dogBreeds.includes(pet)) { break; return "Not All Dogs" }
   }
   // can be written as
   return pets.every(pet => dogBreeds.includes(pet)) ? "Dog" : "Not All Dogs";

7/ Optional Chaining and Nullish Coalescing

By the time writing this post, optional chaining and nullish coalescing are not by default, in ES yet. In order to use, we need to use Babel settings. I hope that in the future, it can be integrated into ES because these validations are encountered very frequently, especially when validation JSON data from backend APIs.

Let’s look at one example below, this example is taken from

const dog = {
  breed: "Alaska",
  detail: {
    name: "Milky",
    body: {
      color: "white",
      weight: 25,
      age: 5
    }
  }
}

// to get the dog breed
const breed = dog && dog.breed || "default breed";
// to get the dog color
const color = dog && dog.detail && dog.detail.body && dog.detail.body.color || "default color";

This can be re-written as

// to get the dog breed
const breed = dog?.breed ?? "default breed";
// to get the dog color
const color = dog?.detail?.body?.color ?? "default color";

 You can check the optional chaining here and the nullish coalescing here.

Categories:

One response

Leave a Reply

Your email address will not be published. Required fields are marked *