363

How can I define conditional array elements? I want to do something like this:

const cond = true;
const myArr = ["foo", cond && "bar"];

This works as expected and results in ["foo", "bar"] but if I set cond to false, I get the following result: ["foo", false]

How can I define an array with a conditional element?

1

14 Answers 14

800

You can spread an array inside of an array, in order to keep items array clean, when the condition is false.

Here's how you can do it:

// Will result in ['foo', 'bar']
const items = [
  'foo',
  ... true ? ['bar'] : [],
  ... false ? ['falsy'] : [],
]

console.log(items)

Explanations:

As you can see the ternary operator always returns an array.

If the condition is true, then it returns ['bar'], otherwise an empty array [].

After that we spread out ... the resulted array (from the ternary operation) and the array's items are pushed to the parent array.

If there aren't any array items (when the ternary check is false), then nothing will be pushed, which is our goal.


In other answer I explained the same idea, but for objects. You can check it too here.

Sign up to request clarification or add additional context in comments.

11 Comments

I recently needed to do this and using spread seemed like the most obvious solution to me. Glad others are using this pattern as well. I didn't want to make it too cryptic but I'm going to run with it since it seems popular enough.
Is there anyway to not add anything if it's false I've tried the above as well as putting null but it increases the size of the array. For example: condition ? doThis() : null
Didn't understand your question. Can you please explain again? Thanks!
I'm sorry about my bad explanation, what I'm asking is add if true, don't add anything if false. Example array size = 6 - If statement true > array size 7 - If statement false > array size doesn't change
No worries :) If you run the answer's js example, you will have the result you needed
|
67

I'd do this

[
  true && 'one',
  false && 'two',
  1 === 1 && 'three',
  1 + 1 === 9 && 'four'
].filter(Boolean) // ['one', 'three']

Note that this will also remove falsy values, such as empty strings.

4 Comments

I like the idea. But your implementation would be wrong for [ true && '']. The empty string would be filtered out. I would rather use a helper function like [foo].filter(isNonBoolean) or [foo].stripBoolean()
@Martin Yeah, maybe that could be elaborated in the answer. I'll go ahead and edit it!
I think there will be a problem if i need a array like this: const arr = ['one', 'two', false, true]; But idea is good for truthy value :)
This won't work if you conditionally add an element to a Widget having a children (such as ListView, Column, Row), saying "The operands of the operator '&&' must be assignable to bool.".
20

If you really want to keep it as a one liner, you could use:

const cond = true;
const myArr = ["foo"].concat(cond ? ["bar"] : []);

1 Comment

clever, was useful for me for conditional front of array i.e. const myArr = (cond ? ['item 1'] : []).concat(['item 2', 'item 3']);
16
const cond = false;
const myArr = ["foo", cond ? "bar" : null].filter(Boolean);

console.log(myArr)

Will result in ["foo"]

Comments

9

You don't have so many options other than using push:

const cond = true;
const myArr = ["foo"];

if (cond) myArr.push("bar");

Another idea is potentially adding null's and filtering them out:

const cond = true;
const myArr = ["foo", cond ? "bar" : null];

myArr = myArr.filter((item) => item !== null);

Comments

8

You can try with a simple if :

if(cond) {
    myArr.push("bar");
}

1 Comment

OP is explicitly asking for a declarative way to do this, not an imperative one.
5

There's a few different ways, but the way you're doing it won't really work for Javascript.

The easiest solution would be to just have an if statement.

if (myCond) arr.push(element);

There's also filter, but I don't think that's what you want here at all, since you seem to be going for "Add this one thing, if this one condition is true" rather than checking everything against some condition. Although, if you want to get really freaky, you can do this (would not recommend, but it's cool that you can).

var arr = ["a", cond && "bar"];
arr.filter( e => e)

Basically it will just filter out all the non true values.

3 Comments

this seems cool. you could also do var arr = ["a", cond && "bar"].filter(e => e); @Adam LeBlanc but you are right, it is probably garbage. but cool garbage ;)
It's cool, but I would advise against it for just one conditional value. It's better suited for a general purpose "Get rid of all the false values" type thing. Since you're just going to end up going through the entire array for just one value. But if you have an undetermined amount of conditional values, then it's probably a good solution.
.filter(e => e) would filter out the empty string.
4

If you insist on doing that inline, here is a simple solution that doesn't require looping to filter

let skipNext = true

const arr = [1,2,3,4,5, ...(skipNext ? [7]: [6])]

console.log(arr)

skipNext = false

const arr2 = [1,2,3,4,5, ...(skipNext ? [7]: [6])]

console.log(arr2)

const addVal = true

const arr3 = [1,2,3,4,5, ...(addVal ? [7]: [])]

console.log(arr3)

1 Comment

this is beautiful, thanks!
2

This is an alternative to Jordan Enev's answer if you don't care too much about performance and it feels like you want to learn more about Javascript :)

So, if you want to do this, but without the false/undefined/null element

['foo', true && 'bar', null && 'baz']; // ['foo', 'bar', null]

You can do it like this:

['foo', true && 'bar', ...Object.values({ ...(null && ['baz']) })]; // ['foo', 'bar']

Finally, the positive condition will work as expected:

['foo', true && 'bar', ...Object.values({ ...(true && ['baz']) })]; // ['foo', 'bar', 'baz']

Bonus: if you want to add a thing to an array, but the thing can be falsy and then you don't want it in there, without doing a 2nd operation, here's a way to do it:

const foo = 1; // 1
const bar = (() => 'expensive result that turns out falsy for bar' && false)(); // false
const baz = (() => 'expensive result for baz')(); // 'expensive result'
const conditionalArrayWrapperFor = i => i ? [i] : [];
// tip: you can always inline conditionalWrapper if you only execute it once
// for instance, if you're using this inside a reduce call
[foo, ...conditionalArrayWrapperFor(bar), ...conditionalArrayWrapperFor(baz)] // [1, 'expensive result for baz']

1 Comment

with (truthy/falsy) && 'bar' -- if the first part is falsy - the returned item will be false and will be part of the array...
2

Add elements conditionally

/**
 * Add item to array conditionally.
 * @param {boolean} condition
 * @param {*} value new item or array of new items
 * @returns {array} array to spread
 * @example [ ...arrayAddConditionally(true, 'foo'), ...arrayAddConditionally(false, 'bar'), ...arrayAddConditionally(true, [1, 2, 3]), ...arrayAddConditionally(true, [4, 5, 6], true) ] // ['foo', [1, 2, 3], 4, 5, 6]
 */
export const arrayAddConditionally = (condition, value, multiple) => (
    condition
        ? [value]
        : []
);

Create array with conditional elements


/**
 * Create array with conditional elements
 * @typedef {[condition: boolean, value: any, multiple: boolean]} ConditionalElement
 * @param {(ConditionalElement|*)[]} map non-array element will be added as it is, array element must allways be conditional
 * @returns {array} new array
 * @example createArrayConditionally([[true, 'foo'], [false, 'baz'], [true, [1, 2, 3]], [true, [4, 5, 6], true], {}]) // ['foo', [1,2,3], 4, 5, 6, {}]
 */
export const createArrayConditionally = (map) => (
    map.reduce((newArray, item) => {
        // add non-conditional as it is
        if (!Array.isArray(item)) {
            newArray.push(item);
        } else {
            const [condition, value, multiple] = item;
            // if multiple use value as array of new items
            if (condition) newArray.push[multiple ? 'apply' : 'call'](newArray, value);
        }
        return newArray;
    }, [])
);

Comments

1

Alternative approach: Pre-Filter populate instead of post filtering:

const populate = function(...values) {
    return values.filter(function(el) {
        return el !== false
    });
};

console.log(populate("foo", true && "bar", false && "baz"))

Returns

(2) ["foo", "bar"]

I know that does not solve the shorthand notation (since it won't work no matter what you try) but it comes close to that.

Comments

1

I prefer using this instead of array spreads when working with an array of objects.

Basically, I use empty objects instead of empty arrays and filter that out

[
    {
      title: 'Transfer type',
      value:
        params.title === LocalTransferTitle
          ? 'Local bank transfer'
          : params.title === WalletToWalletTitle
            ? 'Infra-wallet'
            : params.title === PeerToPeerTitle
              ? 'Peer-to-peer'
              : '',
    },
    params.title === LocalTransferTitle
      ? {
          title: 'Recipient Bank',
          value: params.bankName,
        }
      : {},
    params.title === LocalTransferTitle
      ? {
          title: 'Recipient Name',
          value: params.accountName,
        }
      : {},
    {
      title: 'Amount',
      value: numberToCurrency(
        convertToFloat(params.amount),
        params.wallet?.country,
        params.wallet?.currency,
      ),
    },
    params.title === WalletToWalletTitle
      ? {
          title: 'Exchange Rate',
          value: `1 NGN = ${params.receives} ${params.wallet?.currency}`,
        }
      : {},
    params.title !== WalletToWalletTitle
      ? {
          title: 'Transaction Fee',
          value: numberToCurrency(
            params.title === PeerToPeerTitle ? 0 : 10,
            params.wallet?.country,
            params.wallet?.currency,
          ),
        }
      : {},
    params.title !== WalletToWalletTitle
      ? {
          title: 'Note',
          value: params.narration,
        }
      : {},
  ].filter((detail) => Object.keys(detail).length > 0)

Comments

-2

For any foo, bar, baz:

[
  [foo, true],
  [bar, 1 + 1 === 2],
  [baz, false]
]
.filter(tuple => tuple[1])
.map(tuple => tuple[0])

// [foo, bar]

For strings:

const map = {
  foo: true,
  bar: 1 + 1 === 2,
  baz: false,
}
const myArr = Object.keys(map).filter(k => map[k])

// ['foo', 'bar']

Comments

-4

if you are using es6, I would suggest

let array = [ "bike", "car", name === "van" ? "van" : null, "bus", "truck", ].filter(Boolean);

This array will only contain value "van" if name equals "van", otherwise it will be discarded.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.