Conditionally Add to an Object or Array in JavaScript
September 07, 2020
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 && expr2console.log(true && "hi");// hiconsole.log(`abc` && "123");// 123console.log({} && "empty but valid");// empty but validconsole.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);// undefinedconsole.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']