Amberley Romo

Conditionally Add to an Object or Array in JavaScript

September 07, 2020

A screenshot of code showing how to conditionally add to an object or array in JavaScript, explained in the article.

In the course of my work, it's not uncommon that I need to conditionally add properties to objects, or (probably less commonly) values to arrays. Let's talk about how to do both. This is the piece of code I'll refer to:

const trueCondition = true;
const falseCondition = false;
const obj = {
...(trueCondition && { dogs: "woof" }),
...(falseCondition && { cats: "meow" }),
};
// { dogs: 'woof' }
const arr = [
...(trueCondition ? ["dog"] : []),
...(falseCondition ? ["cat"] : [])
];
// ['dog']

First, defining a few things.

If you're familiar with the && operator, the ternary operator, and spread syntax, skip ahead.

The logical && (AND) operator

&& is a logical operator. Logical operators are used to "reason" about Booleans. The && operator is one of three available in JavaScript (Not material here, but for completeness -- the two others are the || (OR) operator and ?? (nullish coalescing) operator.).

Usage

// expr1 && expr2
console.log(true && "hi");
// hi
console.log(`abc` && "123");
// 123
console.log({} && "empty but valid");
// empty but valid
console.log(false && "bye");
// false

If the first expression (on the left side) is truthy ("considered true when encountered in a Boolean context"), return the second expression (on the right side).

If the first expression is falsy ("considered false when encountered in a Boolean context"), return the first expression.

Short-circuit evaluation

The && expression is evaluated left to right. If the first expression is falsy, the full expression is short-circuit evaluated to the falsy expression (meaning the second expression is never evaluated). This is what lets us do things like safely access nested properties on an object:

const obj = {};
console.log(obj.first && obj.first.second);
// undefined
console.log(obj.first.second);
// TypeError: Cannot read property 'second' of undefined

The conditional (ternary) operator

The ternary operator can be thought of as a shortcut for the if statement. It's made of of three parts:

  • A condition followed by a question mark (?)
  • An expression to execute if the condition is truthy, followed by a colon (:)
  • an expression to execute if the condition is falsy
// condition ? exprIfTrue : exprIfFalse

An example. The two functions below accomplish the exact same thing using different syntax. The first uses if logic, and the second uses a ternary

function getWelcomeMessage(isLoggedIn) {
if (isLoggedIn) {
return "Welcome!";
} else {
return "Please log in.";
}
}
console.log(getWelcomeMessage(true));
// Welcome!
console.log(getWelcomeMessage(false));
// Please log in.
function getWelcomeMessageTernary(isLoggedIn) {
return isLoggedIn ? "Welcome!" : "Please log in.";
}
console.log(getWelcomeMessageTernary(true));
// Welcome!
console.log(getWelcomeMessageTernary(false));
// Please log in.

The spread operator (...)

Spread syntax can be used to expand an iterable (like an array expression), or expand object properties.

Spreading an iterable:

let myDogs = [`Riggins`, `Lyla`];
let parentsDogs = [`Ellie`, `Remi`];
const holidayDoghouse = [...myDogs, ...parentsDogs];
// [ 'Riggins', 'Lyla', 'Ellie', 'Remi' ]

Spreading object properties:

let existingAnimals = {
dogs: 2,
cats: 4,
donkeys: 2,
horses: 2,
};
let newAnimals = {
goats: 2,
};
const allAnimals = {
...existingAnimals,
...newAnimals,
};
// { dogs: 2, cats: 4, donkeys: 2, horses: 2, goats: 2 }

It can be used on iterables like an array or a string. It expands an iterable to its individual elements

Conditionally add a property to an object

To conditionally add a property to an object, we can make use of the && operator.

const trueCondition = true;
const falseCondition = false;
const obj = {
...(trueCondition && { dogs: "woof" }),
...(falseCondition && { cats: "meow" }),
};
// { dogs: 'woof' }

In the example above, in the first property definition on obj, the first expression (trueCondition) is true/truthy, so the second expression is returned, and then spread into the object.

In the second property definition, the first expression (falseCondition) is false/falsy, and so the first expression is returned (and the second expression is never evaluated, because of short-circuiting). It may seem a little confusing to spread a falsy expression, but the result is that it is ignored:

const spreadFalsy = {
...false,
...null,
...undefined,
};
console.log(spreadFalsy);
// {}

You don't need parentheses in evaluating these expressions, but I prefer them, to make it clear that the spread operation applies to the result of the full expression.

const trueCondition = true;
const falseCondition = false;
const withParentheses = {
...(trueCondition && { dogs: "woof" }),
...(falseCondition && { cats: "meow" }),
};
// { dogs: 'woof' }
const withoutParentheses = {
...trueCondition && { birds: "tweet" },
...falseCondition && { foxes: "???" },
};
// { birds: 'tweet' }

Conditionally add a value to an array

Conditionally adding a value to an array looks a little different. Rather than using an && operator, we use a ternary operator.

Unlike the object spread example, if you attempt to spread on a falsy value in an array, you'll get a TypeError:

const falseCondition = false;
const arr = [...(falseCondition && ["cat"])];
// TypeError: boolean false is not iterable

Hence we need a ternary; Using a ternary, we can fall back to spreading an empty array. Then (assuming we've correctly provided two possible iterables) both possible returned expressions will be iterables:

const trueCondition = true;
const falseCondition = false;
const arr = [
...(trueCondition ? ["dog"] : []),
...(falseCondition ? ["cat"] : [])
];
// ['dog']

© 2023 Amberley Romo