Does Javascript have the equivalent of Python’s list comprehension?

Asked

Viewed 92 times

1

I have the following code in Python:

tag = 3
stack = [1,2,3,4,5,3,2,4,55,3,4,3]
results = [i for i, x in enumerate(stack) if x == tag]

This code will generate the following list: [2, 5, 9, 11].

And I want to do the same in Javascript. I know how to do:

var tag = 3;
var stack = [1,2,3,4,5,3,2,4,55,3,4,3];
var results = [];
for (var i = 0 ; i < stack.length ; i++) {
    if(stack[i] == tag) {
        results.push(i);
    }
}
console.log(results);

But it has how to do the same elegant way that is done in Python?

2 answers

3


What was done in Python is called list comprehension. This syntax does not exist in Javascript, but you can get similar results with methods like map, filter and reduce, although "elegant" is arbitrary.

Example:

tag = 3
stack = [1, 2, 3, 4, 5, 3, 2, 4, 55, 3, 4, 3]
result = stack.reduce((arr, x, i) => x == tag ? arr.concat(i) : arr, [])
      
console.log(result)

2

In Python this is called comprehensilist on, and it is something that Javascript does not have (it has already "got" more or less, as we will see below).

So the way is to make one loop even normal, as you have already done. Of course you can do in other ways, such as using map and filter:

var tag = 3;
var stack = [1, 2, 3, 4, 5, 3, 2, 4, 55, 3, 4, 3];
var results = stack.map((_, indice) => indice).filter(i => stack[i] == tag);
console.log(results); // [ 2, 5, 9, 11 ]

The map maps the elements to their respective indexes, and the filter filters those who satisfy the condition.

But I find it an unnecessary complication. Your code I consider the best option, because it is simpler, readable and without unnecessary "wrinkles" - I know that the code is "bigger" than the other options, but this should not be the only criterion to choose between one or the other. Smaller code is not necessarily better.

Using map and filter, you end up making two loops (for an array with N elements, 2N iterations are made), and each of these functions returns another array. In the above case, map generates an "intermediate" array, which is used by filter, only then to generate the final array with the results. Not to mention that both receive as a parameter a function of callback, which is executed for each element of the array (ie for an array with N elements, will be 2N function calls: N calls to the callback of map and more N calls to the callback of filter).

The same goes for reduce of another answer: although it only traverses the array once, yet for each element a function call is made. Only this makes these solutions slower.

I took a basic test and its loop simple was by far the fastest of all. Already the reduce was the slowest (much more, inclusive - tested in Chrome, may vary depending on the environment).

Of course, for small arrays being processed a few times, the difference will be inconspicuous. But it’s important to keep these details in mind, and not use something just because the code gets smaller, or because it’s "cooler," or because "functional is better," etc. The code may even get smaller, but it will often get worse (example).


Javascript already "had" this, but no more...

Just as a curiosity, there was a proposal to include something similar in Javascript (called "Array comprehension"), however never officially incorporated into the language. You can see the documentation on Web Archive (for the original MDN link nor exists anymore).

There we can see that there is a warning saying that it is not a standardized functionality, and that it came to work for some time in some browsers (is mentioned Firefox, from versions 30 to 58), but was later removed, and remains so to this day.

In the Web Archive we can also see some examples of the syntax, very close to Python:

[for (x of iterable) x]
[for (x of iterable) if (condition) x]
[for (x of iterable) for (y of iterable) x + y]

The difference, as you can see, was that the expression was at the end, while in Python it is at the beginning. That is, in Python it would be like this:

numbers = [1, 2, 3, 21, 22, 30]
doubledEvens = [i * 2 for i in numbers if i % 2 == 0]
print(doubledEvens); # [4, 44, 60]

While the Javascript equivalent would look like this:

var numbers = [1, 2, 3, 21, 22, 30];
var doubledEvens = [for (i of numbers) if (i % 2 === 0) i * 2];
console.log(doubledEvens); // [4, 44, 60]

An interesting detail is that to iterate through the elements, only the for...of was allowed.

And older versions of the proposal still suggested a syntax similar to that of Python (with the expression at the beginning, before the for).

However none of this went forward and Javascript currently does not have such feature. Therefore, the alternatives are those already listed above and in another answer (or the loop simple that you did, that - I repeat - I still think the best option, for the reasons already mentioned).

  • You dug it up from where? D Never seen it before...

  • @Luizfelipe It was kind of random, I was researching something else and unintentionally bumped into the subject. Then I remembered this question and decided to answer :-)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.