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 ultimately use as an example:
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 ? exprIfConditionTrue : exprIfConditionFalse
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
/*
* Example 1
*/
function getWelcomeMessage(isLoggedIn) {
if (isLoggedIn) {
return "Welcome!";
} else {
return "Please log in.";
}
}
console.log(getWelcomeMessage(true));
// Welcome!
console.log(getWelcomeMessage(false));
// Please log in.
/*
* Example 2
*/
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']
Top comments (1)
Thank you for this awesome post! Iโve racking my brain trying to remember the name of the
...
operator ๐