# List Comprehension

**URL:** <https://es.discourse.group/t/list-comprehension/112>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [October 25, 2019, 4:26pm UTC](https://es.discourse.group/t/list-comprehension/112 "2019-10-25T16:26:47Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Maxwell-Alexius](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/maxwell-alexius/32/145_2.png) [@Maxwell-Alexius](https://es.discourse.group/u/Maxwell-Alexius)\
**Post date:** [October 25, 2019, 4:26pm UTC](https://es.discourse.group/t/list-comprehension/112/1 "2019-10-25T16:26:47Z")

</div>

## Proposal - List Comprehension

Hi, I have an idea to propose but not sure how it can be implemented. Still, I wanted to contribute a new syntax feature for ECMAScript with a functional programming feature: "List Comprehension".

### Introduction

I know there are languages adopt the feature such as Python:

```python
aList = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in aList]

```

'aList' is the existing list, but we can use the **List Comprehension** syntax to create another list.

Simply put, it is a feature for creating a list (or array) structured object from another list structured object.

I know we can already use basic syntax in JS like the operation `Array.prototype.map`, but I wanted to propose a more advanced use case which combines with the ESMAScript standard.

Additionally, I searched from the internet and found out that the [MDN doc already has a similar feature called "Array Comprehension"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Array_comprehensions). Again, I know this feature is obsoleted in the browser implementation, but I wanted to reference how this feature has experimented in the past.

The examples from the docs are:

```javascript
/* Normal Case */
[for (i of [1, 2, 3]) i * i ]; 
// [1, 4, 9]

var abc = ['A', 'B', 'C'];
[for (letters of abc) letters.toLowerCase()];
// ["a", "b", "c"]

/* Multiple Parameters Case */
var numbers = [1, 2, 3];
var letters = ['a', 'b', 'c'];

var cross = [for (i of numbers) for (j of letters) i + j];
// ["1a", "1b", "1c", "2a", "2b", "2c", "3a", "3b", "3c"]

/* Conditional Case */
var years = [1954, 1974, 1990, 2006, 2010, 2014];
[for (year of years) if (year > 2000) year];
// [2006, 2010, 2014]
[for (year of years) if (year > 2000) if (year < 2010) year];
// [2006], the same as below:
[for (year of years) if (year > 2000 && year < 2010) year];
// [2006] 

```

The following contents are about what I have come up with.

### Basic Syntax

I think the proper way is to represent in a more mathematical way:

```javascript
[i * i | for (i of [1, 2, 3]) ]; 
// [1, 4, 9]

var abc = ['A', 'B', 'C'];
[letters.toLowerCase() | for (letters of abc)];
// ["a", "b", "c"]

```

Secondly, instead of using multiple `for...of...` expressions, we can simply use a little bit of _destructuring_ style to simplify the cases:

```javascript
/* Multiple Parameters Case */
var numbers = [1, 2, 3];
var letters = ['a', 'b', 'c'];

var cross = [i + j | for ([i, j] of [numbers, letters])];
// ["1a", "1b", "1c", "2a", "2b", "2c", "3a", "3b", "3c"]

```

However, it is important to note that `i` represented the elements iterated from the `numbers` array; `j` represented the elements iterated from the `letters` array.

Lastly, when it comes to the conditional case, I think we can do something like:

```javascript
/* Conditional Case */
var years = [1954, 1974, 1990, 2006, 2010, 2014];
[year | for (year of years) if (year > 2000)];
// [2006, 2010, 2014]
[year | for (year of years) if (year > 2000 && year < 2010) year];
// [2006] 

```

Additionally, the conditional case above just filter the element out of the list and didn't do any additional computation. I think it can be simplified like this:

```javascript
/* Conditional Case */
var years = [1954, 1974, 1990, 2006, 2010, 2014];
[for (year of years) if (year > 2000)];
// [2006, 2010, 2014]
[for (year of years) if (year > 2000 && year < 2010) year];
// [2006] 

```

However, if it didn't have the conditional case and didn't do any computations, such as:

```javascript
[i | for (i of [1, 2, 3]) ]; 
// [1, 2, 3]

```

I think it shouldn't be simplified in:

```javascript
[for (i of [1, 2, 3]) ]; 
// Wrong case

```

Reasons are:

1. `i` isn't used, so declaring `i` doesn't have any meaning
2. The operation above is the same as cloning an array which I think it could just use `[...[1, 2, 3]]`

However, if it comes to the multiple parameter case, then:

```javascript
// Finding the coordinates in the first coordinate quadrant
[[x, y] | for ([x, y] of [xPositions, yPositions]) if (x > 0 && y > 0) ]

```

Can be reduced as:

```javascript
// Finding the coordinates in the first coordinate quadrant
[for ([x, y] of [xPositions, yPositions]) if (x > 0 && y > 0) ]

```

Since the destructured format `[x, y]` in `for ... of ...` matches the desired result, the "`[x, y] |`" can be omitted.

### May be confused with Bitwise OR operator? (which is `|`)

I think this might not be the problem, because in this article, the list comprehension syntax idea, the `|` should always followed by the keyword `for`.

```javascript
[a | b | for ([a, b] of [list1, list2]) ]

```

However, it might be best to distinguish by using parentheses:

```javascript
[(a | b) | for ([a, b] of [list1, list2]) ]

```

### Alternative List Comprehension Syntax which I've Came Up With

1. Omit the `|`

```javascript
[x ** 2 for (x of aList)]

```

1. Using `where`

```javascript
[x ** 2 | where (x of aList)]

```

1. Omit the `|` and using `where`

```javascript
[x ** 2 where (x of aList)]

```

But I personally still wanted to stick with `for...of...` syntax because of the following cases which combines with ECMAScript standards I'm going to address.

### Combining with ECMAScript Standards

The above are the basic cases are covered. I know we can use similar approaches such as `Array.prototype.map` or `Array.prototype.filter` to deal with list-like structure creation.

However, since more and more ECMAScript standard covered features such as **ES6 Iterators and Generators** , **ES7 Async-Await** and **ES9 Asynchronous Iterations** , I have several ideas came up.

#### Feature 1. Use _Iterator object_ as the existing list

```javascript
function *genExample() {
  yield 1;
  yield 2;
  yield 3;
}

[x ** 2 | for (x of genExample())];
// [1, 4, 9]

```

`for...of...` loop can accept and iterate through objects with `Symbol.iterator` property (because it can generate the Iterator object).

And if iterator object is acceptable, which means:

```javascript
// Originally JSON object isn't iterable
let info = { name: 'Max', age: 20, hasPet: false };

// We can declare its own generator function to generate the iterator
info[Symbol.iterator] = function*() {
  for (prop in this){
     yield [prop, this[prop]]
  }
}

// It can now be plugged into the for...of... loop
for (let [p, v] of info) { console.log(p, v) }

// Which means it can also be used in List Comprehension:
[`${prop} is ${value}` | for [prop, value] of info ]

```

#### Feature 2. Generating an iterator from List Comprehension

Similar to how we declare the generator function, we can use a similar approach to declare an iterator object:

```javascript
const list = [1, 2, 3];
let iterator = *[i ** 2 | for (i of list)];

// Using for...of... loop
for (let i of iterator) {/* ... */}

// Or simply use the built-in APIs like the next method, etc...

```

### Feature 3. Await the `Promise.all` like list structure

This feature is considered a little hard for me to come up with. Overall, I reckon that this might be a case where we can combine the `Promise.all` utility with an expression to create a new array data structure.

```javascript
async function example() {
  const responses = await [res.data | for (res of requests)];
  // Do something...
}

```

### Feature 4. Use "Asynchronous Iterator" as the existing list

This feature states that it generates the asynchronous iterator via list comprehension.

```javascript
const lines = async *[line.trim() | for await (line of readline)];

```

Basically, its a bit like generating an iterator from this asynchronous function:

```javascript
async function* () {
  for await (const line of readline) {
    yield line.trim();
  }
}

```

### Adopt Function Programming Feature

#### 1. [Eta Conversion](https://wiki.haskell.org/Eta_conversion)

JavaScript already had embraced this feature, for instance:

```javascript
// Declare a function which calculates the squared value of number
function squared(value) { return value ** 2; }

const list = [1, 2, 3];

// Map each element with squared value
list.map(x => squared(x));

```

Can be simplified into:

```javascript
list.map(squared)

```

Because it applies the elements from the array and directly inputs into another function.

So, as in the list comprehension case, the code:

```javascript
[squared(x) | for (x of list)];

```

Can also considered to simplified into this way:

```javascript
[squared | for (x of [1, 2, 3]) ];

```

But then the `x` will violate the rule which I have mentioned in this article. The `x` isn't used in this case. So it kind of a bit awkward to come up with another solution:

```javascript
[squared | for (list)]

// It should also be the same as:
// [squared | for list]
//

```

Well, I'm not sure if this is appropriate, but please see the next feature, although it could be a very far future to propose and entering the implementation phase.

#### Feature 2. Function Composition in List Comprehension

Normally, in mathematics, assume we have function `f(x)` and `g(x)`, we can use `f・g` to represent `f(g(x))`.

To achieve function composition with list comprehension idea in this article:

```javascript
function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
  return str + '!';
}

let names = ['maxwell', 'martin', 'leo'];

[exclaim(capitalize(name)) | for (name of names)]

```

If we want to do it in a more elegant way by using Eta conversion, we need to do something like:

```javascript
function customConversion(str) {
  return exclaim(capitalize(str));
}

[customConversion | for names]

```

However, considering there are [existing proposals focusing on pipeline operator](https://github.com/tc39/proposal-pipeline-operator) which is about function composition, I think we can simplify to:

```javascript
[capitalize |> exclaim | for names]

```

## Summary

I think that's all, this is my idea about contributing ECMAScript by proposing List Comprehension feature.

I know this article is long, but your patience and kind feedbacks would be very appreciated. :)

---

<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 25, 2019, 10:26pm UTC](https://es.discourse.group/t/list-comprehension/112/2 "2019-10-25T22:26:11Z")

</div>

See also:

- [https://esdiscuss.org/topic/array-comprehensions](https://esdiscuss.org/topic/array-comprehensions)

- [https://esdiscuss.org/topic/array-comprehensions-shorter-syntax](https://esdiscuss.org/topic/array-comprehensions-shorter-syntax)

- [https://github.com/tc39/tc39-notes/blob/c4466ea1977d17a0fa607e3ab9ff63504dba004f/es6/2014-06/jun-5.md#generator-comprehensions-slides-plz](https://github.com/tc39/tc39-notes/blob/c4466ea1977d17a0fa607e3ab9ff63504dba004f/es6/2014-06/jun-5.md#generator-comprehensions-slides-plz)

- [https://speakerdeck.com/dherman/a-better-future-for-comprehensions](https://speakerdeck.com/dherman/a-better-future-for-comprehensions)

- [https://github.com/tc39/tc39-notes/blob/c4466ea1977d17a0fa607e3ab9ff63504dba004f/es6/2014-07/jul-30.md#47](https://github.com/tc39/tc39-notes/blob/c4466ea1977d17a0fa607e3ab9ff63504dba004f/es6/2014-07/jul-30.md#47)

- [https://esdiscuss.org/topic/comprehensions-where-art-thou](https://esdiscuss.org/topic/comprehensions-where-art-thou)

- [https://esdiscuss.org/topic/array-comprehensions-with-spread-operator](https://esdiscuss.org/topic/array-comprehensions-with-spread-operator)

- [https://esdiscuss.org/topic/a-reprieve-for-es6-comprehensions](https://esdiscuss.org/topic/a-reprieve-for-es6-comprehensions)

---

<div class="post-metadata">

**Author:** ![Jonas\_Wilms](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jonas_wilms/32/189_2.png) [@Jonas\_Wilms](https://es.discourse.group/u/Jonas_Wilms)\
**Post date:** [October 28, 2019, 3:50pm UTC](https://es.discourse.group/t/list-comprehension/112/3 "2019-10-28T15:50:13Z")

</div>

First of all, I generally like the idea of simplyfiying the process of building up arrays from logic.

However, I do have a few concerns:

`for`, `async` and others are fundamental _statements_ of the language, now they are suddenly used as _expressions_ . The only way I can think of, for this to work in the way you propose it, is to introduce a whole new set of syntax, that redefines these keywords.

That's a lot of new syntax, just to support little new functionality ... I mean, there is already:

```javascript
const result = [...(function*() {
  for (year of years) if (year > 2000 && year < 2010) yield year;
})()];

```
