# Conditionally add elements to declaratively defined arrays

**URL:** <https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041>\
**Category:** 💡 Ideas\
**Created:** [October 20, 2021, 1:49am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041 "2021-10-20T01:49:21Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![aiddun](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aiddun/32/1073_2.png) [@aiddun](https://es.discourse.group/u/aiddun)\
**Post date:** [October 20, 2021, 1:49am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/1 "2021-10-20T01:49:21Z")

</div>

Hi all! I had an idea for a feature that I would be a really great addition to the language, and was curious on others' thoughts.

As declarative Javascript is used more and more, there is no easy way to conditionally add an element to an array in a declarative way.

I am proposing an "empty spread" operator, that allows one to return a "no elements" expression in a syntax similar to the current spread operator. Here's an example:

```javascript
// current ways
const pets = [
  "Dogs",
  "Hamster",
  "Penguin",
  ...(hasCats ? ["Cats"] : []),
];

let pets = ["horse];
if (horses) {
  pets.push(horses);
}

// new way
const pets = [
  "Dogs",
  "Hamster",
  "Penguin",
  hasCats ? "Cats" : ...,
];

pets = ["horse", cow ?? ...];

```

See [here](https://2ality.com/2017/04/conditional-literal-entries.html) for more examples. It is currently possible but really hard to read as a human.

I believe that an easy way to conditionally declaratively add something to an array or map would be great for reducing bugs and code complexity from two fronts. First, it would allow more code to be written declaratively and immutably. Second, it would make declarative code easier to read in situations like this.

This operator would have the same psudo-LHS characteristics as the normal spread operator - it cannot be assigned to a variable, nor returned by a function. I believe that JavaScript already has enough types to represent a value lacking some sort of trait (null, undefined), and making this its own type would add complexity to code. These operators should only be allowed to be defined within literal array and object field blocks.

A corollary is doing the same for objects. While in a lot of code patterns undefined in an object lookup is seen as there being no key, the `in` keyword and `hasOwnProperty` function actually return true if there is a key set to undefined in an object.

Syntax Possibilities I've thought of:

```javascript
… - Most compact solution with spread operator
(…) - Wider empty expression with spread operator with parenthesis providing separation from potential camoflauged commas
[…], {…} - Case specific operators, with relations to expression, potentially confusing
() - Signifies an empty expression, also potentially confusing for use

```

I'm working on a babel polyfill proof-of-concept [here](https://github.com/aiddun/babel/tree/proposal-empty-spread) right now, most things work, there are a few bugs I'm trying to squash with the ... operator in babel.

---

<div class="post-metadata">

**Author:** ![lightmare](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/lightmare/32/843_2.png) [@lightmare](https://es.discourse.group/u/lightmare)\
**Post date:** [October 20, 2021, 4:51am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/2 "2021-10-20T04:51:32Z")

</div>

> [@aiddun](#):
>
> `… - Most compact solution with spread operator`

Most compact would be having no token at all ;)

```javascript
["dogs", hasCats ? "cats"]
["horse", cow ??]

```

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [October 20, 2021, 7:00am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/3 "2021-10-20T07:00:59Z")

</div>

```javascript
[].concat(a, b ?? [], c || [])

```

This will add `b` to the array if it's non-nullish, and `c` if it's truthy, and the array will end up with 1, 2, or 3 elements accordingly.

---

<div class="post-metadata">

**Author:** ![sirisian](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/sirisian/32/787_2.png) [@sirisian](https://es.discourse.group/u/sirisian)\
**Post date:** [October 20, 2021, 7:26pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/4 "2021-10-20T19:26:10Z")

</div>

I've had the same need for this in my code a few times. If it's added for arrays, I'd argue it should also be added for function arguments:

```javascript
function f(...a) { return a.length; }
const b = null;
// f(0, 1, ...[b ??]); // 2
f(0, 1, b ??); // 2

```

I quite like lightmare's proposal. Very readable.

---

<div class="post-metadata">

**Author:** ![aiddun](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aiddun/32/1073_2.png) [@aiddun](https://es.discourse.group/u/aiddun)\
**Post date:** [October 20, 2021, 10:27pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/5 "2021-10-20T22:27:22Z")

</div>

Thanks for the feedback lightmare, @ljharb, and @sirisian!

I believe the argument of just using `concat` instead conflicts with the addition and heavy use of ES6's spread operator. The spread operator offers a concise readable syntax when adding elements to an array or map when `concat` could also work, and also allowing for for arbitrary ordering.

In addition, I believe the concat solution is suboptimal for readability the same way that `…(a ?? [])` is, and how the spread operator isn’t. Another programmer reading the code with a concat needs to keep an internal stack for understanding the layering of components in order to realize nothing will be added to the array.

Furthermore, as I mentioned before, there is no way to choose where in the array you want to conditionally add an object to. This could be important for things like element ordering in UIs. To my knowledge this case is not surmountable with something like chained calls to an array function like splice, as the addition of one element to an array can change the index to add a next one, such as in `[a, b, c ??, d ??, e]`.

There also isn’t a function to my knowledge that would allow this behavior for function arguments, leading to the same readability issues as in 2 paragraphs above. There’s also a potential issue of loss of immutability with that solution, which is of debatable importance.

Per lightmare's suggestion and sirisan's comment, I also really like it - I was originally concerned about readability with a very short/no operator, however I think it has enough contrast with spaces and the `?` while staying concise that it could be the most readable. Once concern I have is if you want to not add something to an array on a true case, where extending this syntactically would be¸ `a ? : c` which I don't think is very readable, however the programmer can just invert the value of their expression. I also like sirisian's extension for function arguments. I try out the polyfill for this, I don't think it will be that hard after now knowing how to do it for '...'.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [October 20, 2021, 10:29pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/6 "2021-10-20T22:29:35Z")

</div>

Here's a related thread: [Optional keys in objects](https://es.discourse.group/t/optional-keys-in-objects/523)

---

<div class="post-metadata">

**Author:** ![daniel.rentz](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/daniel.rentz/32/1946_2.png) [@daniel.rentz](https://es.discourse.group/u/daniel.rentz)\
**Post date:** [October 23, 2021, 8:31am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/7 "2021-10-23T08:31:56Z")

</div>

I would like to see a consistent solution across all places where I can use the spread operator (objects, arrays, and function calls).

I think postfix `?` or `??` would fit better than spread operator, because the latter stands for "expand something" rather than "leave out something".

Examples:

```javascript
const result = someFn();

const arr = [1, 2, result??, ...otherArr];
const obj = { a: 1, b: 2, c: result??, ...otherObj };
otherFn(1, 2, result??, ...otherArgs);

```

Things to think about:

- `?` or `??`? I think `??` is best to reflect that it is about nullish values.

- `undefined` only, or nullish? Maybe not introduce a third concept of "falsy" and stick to nullish.

- shorthand property syntax: `{ result?? }`

- operator after object key? `{ key??: result }`

---

<div class="post-metadata">

**Author:** ![som](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/som/32/1372_2.png) [@som](https://es.discourse.group/u/som)\
**Post date:** [May 9, 2022, 1:39am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/8 "2022-05-09T01:39:35Z")

</div>

Thumbs up from me. Completely agree on the usefulness of this proposal. It addresses a regular irritation. Personally I like the 'sans token' suggestion from @lightmare - it feels like it could work broadly (and elegantly) with most operators and concepts (@aiddun the `a ?: c` case does actually feel legible / logical to me .. tho note a lack of space between `?` and `:`). Also prefer that it is invoked on the value side, as this works for `[]` and `Set()`.

So ..

```javascript
const pets = ["Dogs", hasCats ? "Cats"] // a ? b : <empty>
|| ["Dogs", noCats ?: "Cats"] // a ? <empty> : b
|| ["horse", cow ??] // a ?? <empty>
|| {
  dogs: true,
  ...defaultCatsAndPigs,
  cats: hasCats ||, // a || <empty>
  pigs: hasPigsOrNull ?? } // b ?? <empty>
|| new Set(['Dogs']).add(hasCats ? "Cats") // <empty> ignores 'add'
|| new Map([["cats", 1]]).set('cats', lostCat ? 0) // <empty> ignores 'set'

```

That said, given the hell this kind of 'missing' syntax would play with the language, I wonder if a suitable compromise could be achieved with a new primitive or global object, rather than an operator ... e.g. an \<`empty`\> keyword. Polyfills / pre-processors could step in to afford the above syntax or similar.

To be completely honest, i'd also be mostly happy with it only working on `{ key: value }` objects in the short term, as array-like iterables are easier to work around with `concat([])` hacks.

---

<div class="post-metadata">

**Author:** ![william](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/william/32/2023_2.png) [@william](https://es.discourse.group/u/william)\
**Post date:** [March 6, 2024, 9:19am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/9 "2024-03-06T09:19:23Z")

</div>

Dart has this feature and they call it "Collection control-flow operators", see: [https://dart.dev/language/collections#control-flow-operators](https://dart.dev/language/collections#control-flow-operators)

The specific one discussed here would be their **collection if**. Their example, translated to JS, would look like:

```javascript
const nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];

```

They also have a **collection for** , which would look like this instead:

```javascript
const listOfInts = [1, 2, 3];
const listOfStrings = ['#0', for (let i of listOfInts) `#${i}`];
assert(listOfStrings[1] === '#1');

```

It would be amazing to see this added to JS 😄

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [March 6, 2024, 6:44pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/10 "2024-03-06T18:44:53Z")

</div>

You can already do this, since at least ES3:

```javascript
const nav = [].concat('Home', 'Furniture', 'Plants', promoActive ? 'Outlet' : []];

const listOfInts = [1, 2, 3];
const listOfStrings = [].concat('#0', listOfInts.map(i => `#${i}`)];
assert(listOfStrings[1] === '#1');

```

---

<div class="post-metadata">

**Author:** ![aiddun](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aiddun/32/1073_2.png) [@aiddun](https://es.discourse.group/u/aiddun)\
**Post date:** [March 6, 2024, 8:27pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/11 "2024-03-06T20:27:50Z")

</div>

@ljharb I agree, but this doesn't work for cases where there are many conditional elements at different positions in a list surrounded by constant ones.

For example, we have a piece of code at work that's like

```js
const onboardingComponents = [
Welcome,
...(isFirefox ? EnableFirefoxPersistance : [])
EnableNotifications,
CreateAccount.
...(isDesktop ? [] : EnablePWA),
...(isEurope? GDPRAccept : []),
VerifyEmail,
...(is2FASMS ? Set2FA : [])
]

```

Which could be much easier to write and understand with this syntax.

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [March 6, 2024, 11:08pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/12 "2024-03-06T23:08:08Z")

</div>

Yes, it precisely works for that case:

```javascript
const onboardingComponents = [].concat(
Welcome,
isFirefox ? EnableFirefoxPersistance : [],
EnableNotifications,
CreateAccount,
isDesktop ? [] : EnablePWA,
isEurope? GDPRAccept : [],
VerifyEmail,
is2FASMS ? Set2FA : [],
);

```

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [March 7, 2024, 5:01am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/13 "2024-03-07T05:01:33Z")

</div>

While ternarys are a way to do this today, I have found myself wanting to be able to avoid the extra spread noise when optionally defining object properties:

Say there is existing config:

```javascript
const config = {
  a: 1,
  b: {
    c: 3,
  }
};

```

Many times I have seen it evolve into:

```javascript
const config = {
  a: 1,
  ...(x ? { d: 4 } : {}),
  b: {
    c: 3,
    ...(y ? { e: 5 } : {}),
  }
};

```

When maybe it could have become something like:

```javascript
const config = {
  a: 1,
  d?: x ? 4 : undefined,
  b: {
    c: 3,
    e?: y ? 5 : undefined,
  }
};

```

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/senocular/32/642_2.png) [@senocular](https://es.discourse.group/u/senocular)\
**Post date:** [March 7, 2024, 12:41pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/14 "2024-03-07T12:41:13Z")

</div>

Maybe a discard assignment a la [discard bindings](https://github.com/tc39/proposal-discard-binding)?

```javascript
const config = {
  a: 1,
  d: x ? 4 : void,
  b: {
    c: 3,
    e: y ? 5 : void,
  }
};

```

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [March 7, 2024, 4:11pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/15 "2024-03-07T16:11:27Z")

</div>

I am wildly in favor of that, and it'd also fit very naturally with conceptual object/array comprehensions.

---

<div class="post-metadata">

**Author:** ![aiddun](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aiddun/32/1073_2.png) [@aiddun](https://es.discourse.group/u/aiddun)\
**Post date:** [March 14, 2024, 5:07pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/16 "2024-03-14T17:07:10Z")

</div>

Would any members be interested in championing this proposal?

---

<div class="post-metadata">

**Author:** ![william](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/william/32/2023_2.png) [@william](https://es.discourse.group/u/william)\
**Post date:** [March 14, 2024, 5:29pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/17 "2024-03-14T17:29:09Z")

</div>

What exactly does championing a proposal entail? 🤔

If I could do something to get the Dart-style syntax approved I might be down to try (I feel like the discard binding syntax would still be a bit too unintuitive)

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [March 14, 2024, 5:42pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/18 "2024-03-14T17:42:10Z")

</div>

Championing first requires being a TC39 delegate or invited expert.

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [March 15, 2024, 8:12am UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/19 "2024-03-15T08:12:07Z")

</div>

It's not obvious what the equivalent for arrays should do: create a hole or skip the entry altogether?

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [March 16, 2024, 11:40pm UTC](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041/21 "2024-03-16T23:40:27Z")

</div>

Hole is the closest semantically to objects, but skipping would be less surprising and just all around more useful.

Skipping would let me get rid of _many_ conditional array pushes, and `...(cond ? [entry] : []),` is _not_ very readable at all (but is a common idiom in Redux and the like). And the syntax would be _especially_ valuable for records and tuples.

(Ignore my last message - I didn't read the context closely enough.)

[Next page](https://es.discourse.group/t/conditionally-add-elements-to-declaratively-defined-arrays/1041.md?page=2)
