297

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?

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
Manu Schiller
  • 3,256
  • 2
  • 11
  • 13

12 Answers12

648

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.

Jordan Enev
  • 16,904
  • 3
  • 42
  • 67
  • 2
    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. – Brennan Cheung Dec 05 '18 at 23:55
  • 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` – Muhammad Ali Sep 24 '19 at 18:51
  • Didn't understand your question. Can you please explain again? Thanks! – Jordan Enev Sep 25 '19 at 08:09
  • 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 – Muhammad Ali Sep 25 '19 at 13:50
  • No worries :) If you run the answer's js example, you will have the result you needed – Jordan Enev Sep 25 '19 at 19:54
  • in typescript how would you extract it to an abstract function. I tried this `const fieldGourd = (isShown, field) => (... isShown ? [field] : []);` but i get the error `Tuple type '[]' of length '0' has no element at index '0'.` on the `[field]`, and `'=>' expected.ts(1005)` on the semi-colon at the end – Itay Tur Jul 14 '21 at 09:10
  • argh, i had it `[cond ? ...[item] : ...[]]`. thank you. – gl03 Nov 18 '21 at 11:48
  • FYI, not supported in ie11 – Charles L. Jan 14 '22 at 23:39
  • Note if you always want to push *something* to the array, you can just use the ternary operator and skip the spread bit. `var myArray = [ 1, true ? 3 : null, false ? 'wonderful : '']` – nabrown Aug 27 '22 at 21:58
57

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.

  • 7
    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 Oct 09 '18 at 08:45
  • @Martin Yeah, maybe that could be elaborated in the answer. I'll go ahead and edit it! – Michael Bergquist Suarez Nov 11 '18 at 14:20
  • 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 :) – Sanjib Debnath Aug 07 '20 at 14:11
  • 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.". – Eray Erdin Jun 04 '21 at 10:29
17

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

const cond = true;
const myArr = ["foo"].concat(cond ? ["bar"] : []);
James Thorpe
  • 31,411
  • 5
  • 72
  • 93
  • clever, was useful for me for conditional front of array i.e. `const myArr = (cond ? ['item 1'] : []).concat(['item 2', 'item 3']);` – devonj Dec 06 '19 at 22:08
13
const cond = false;
const myArr = ["foo", cond ? "bar" : null].filter(Boolean);

console.log(myArr)

Will result in ["foo"]

Westman
  • 202
  • 2
  • 4
9

You can try with a simple if :

if(cond) {
    myArr.push("bar");
}
Sylvain Attoumani
  • 1,213
  • 1
  • 15
  • 34
8

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);
Alberto Trindade Tavares
  • 10,056
  • 5
  • 38
  • 46
4

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.

Adam LeBlanc
  • 932
  • 7
  • 21
  • 2
    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 ;) – Manu Schiller Jul 04 '17 at 15:10
  • 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. – Adam LeBlanc Jul 04 '17 at 15:16
  • 1
    .filter(e => e) would filter out the empty string. – Martin Oct 09 '18 at 08:48
2

Add elements conditionally

/**
 * Add item to array conditionally.
 * @param {boolean} condition
 * @param {*} value new item or array of new items
 * @param {boolean} multiple use value as array of new items (for future)
 * @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
        ? multiple ? value : [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;
    }, [])
);

Georgiy Bukharov
  • 356
  • 3
  • 10
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']
tiby
  • 180
  • 1
  • 5
  • with (truthy/falsy) && 'bar' -- if the first part is falsy - the returned item will be `false` and will be part of the array... – Yuval A. May 30 '22 at 12:30
0

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.

Jankapunkt
  • 8,128
  • 4
  • 30
  • 59
-1

How about this:

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

Upd: this would work for strings only.

-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.