Conditionally Add to an Object or Array in JavaScript
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']
Cover image: By Amberley Romo