How to use Object Literals in JavaScript to write complex conditions
Tags: javascript, node, objects, programming
Published: 2021-10-13 06:30:08


Writing complex conditions in JavaScript has always had the potential to create some untidy code. A long list of if/else statements or switch cases can get bloated fast.

When there are multiple conditions, I find object literals to be the most readable way of structuring code. Let’s have a look at how they work.

As an example, let’s say we have a function that takes a dog breed and returns the quote. Using `if/else` statements, it would look like this:

```js
function getDogQuote(breed) {
  if (breed.toLowerCase() === "akita") {
    return "Akitas do speak, but only to those who know how to listen.";
  } else if (breed.toLowerCase() === "beagle") {
    return "Life is better with a Beagle.";
  } else if (breed.toLowerCase() === "dachshund") {
    return "Why do dachshunds wear their ears inside out?";
  } else if (breed.toLowerCase() === "golden retriever") {
    return "The face of a Golden Retriever feels like home.";
  } else if (breed.toLowerCase() === "pug") {
    return "The puggle is real.";
  }

  return "Quote not found";
}
```

This isn’t great. Not only is it not very readable, but we are also repeating `toLowerCase()` for every statement.

We could avoid that repetition by assigning the lowercased breed to a variable at the start of the function or alternatively use a `switch` statement, which would look like this:

```js
function getDogQuote(breed) {
  switch (breed.toLowerCase())) {
    case "akita":
      return "Akitas do speak, but only to those who know how to listen.";
    case "beagle":
      return "Life is better with a Beagle.";
    case "dachshund":
      return "Why do dachshunds wear their ears inside out?";
    case "golden retriever":
      return "The face of a Golden Retriever feels like home.";
    case "pug":
      return "The puggle is real.";
    default:
      return "Quote not found";
  }
}
```

We are now only calling toLowerCase() once, but this still doesn’t feel that readable. switch statements can also be prone to errors. In this case, we are just returning a value, but when you have more complex functionality, it can be easy to miss a break statement and introduce bugs.

## **An Alternative**

You can use an object to achieve the same functionality as above in a much neater way. Let’s have a look at an example:

```js
function getDogQuote(breed) {
  const breeds = {
    "akita": "Akitas do speak, but only to those who know how to listen.",
    "beagle": "Life is better with a Beagle.",
    "dachshund": "Why do dachshunds wear their ears inside out?",
    "golden retriever": "The face of a Golden Retriever feels like home.",
    "pug": "The puggle is real.",
  };
  
  return breeds[breed.toLowerCase()] ?? "Quote not found";
}
```

We have an object where the keys are the conditions and the values are the responses. Then we can use the square bracket notation to select the correct value of the object from the breed passed in.

The final part of line 10 (`?? "Quote not found"`) uses [nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator) to assign a default response. This means that if `breeds[breed.toLowercase()]`is `null` or `undefined` ****(but not `false` or `0` ), then the default string “Quote not found” is returned. This is important because we might legitimately want to return `false` or `0` from our conditional. For example:

```js
function stringToBool(str) {
  const boolStrings = {
    "true": true,
    "false": false,
  };

  return boolStrings[str] ?? "String is not a boolean value";
}
```

This is a very contrived example, but hopefully, it illustrates how nullish coalescing can help avoid introducing bugs!

## **More Complex Logic**

There are times when you might need to do some more complex logic inside your conditions. To achieve this, you can pass a function as the value to your object keys and execute the response:

```js
function calculate(num1, num2, action) {
  const actions = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b,
  };

  return actions[action]?.(num1, num2) ?? "Calculation is not recognised";
}
```

We are selecting the calculation we want to do and executing the response, passing the two numbers. You can use [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) (the `?.` in the last line of code) to only execute the response if it is defined. Otherwise, fall through to the default return string.

## **Conclusion**

Writing conditionals will always be a matter of taste, and certain situations will require a different approach. However, I find that when I have several conditions to check against, object literals are the most readable and maintainable way.

I’d love to hear your thoughts or if you have a different approach than the ones outlined above!