Python map built-in function, why it's superior to javascript array map method and how to implement it

According to python docs map build in function applies a given function to every item of an iterable, yielding the results. map returns an object that is an iterable lazily executing the provided transformation function to each element of input iterable. From the above we understand that map works pretty much like an Generator.

Why is this awesome ?

Well If you were to perform an expensive transformation on a huge iterable just to utilize a sub-part of it, lazily transforming what’s needed is the optimal approach. Let’s have a look at the following example :

1
2
3
4
5
6
7
8
9
10
def isEven(val):
print(F'processing {val}' )
return (val % 2) == 0

huge_range = range(1,10**24+1)

if any(map(isEven, huge_range)):
print('Some even found')
else:
print('No even found')

The above will have the following result:

1
2
3
processing 1
processing 2
Some even found

MyCompiler

As we can see any build in function iterates a given iterable until any truthy value is found and returns true(otherwise it returns false). The only case that our iterable would have all its elements transformed is if no even value was included in it. In our case the second element was even, so we skipped 10^24-2 transformations.

How about Javascript?

In javascript the build in method for mapping is the one of Array.prototype.map. This method is on array prototype (applicable to just arrays) and returns a new array. Since an array is returned transformations get executed all at once. How about lodash helpers? Same goes with lodash supporting just arrays. As a result it’s obvious that if we want to get something similar to python we need to implement our own functions that can work lazily using generators.

The map function

1
2
3
4
5
function* map<a, b>(a: Iterable<a>, f:(a:a) => b){
for (const value of a) {
yield f(value)
}
}

The some function (similar to any in python)

As with map function we don’t have a build in or lodash function that accepts arbitrary iterables so we resort to the following:

1
2
3
4
5
6
7
8
function some<a>(iterable:Iterable<a>) {
for (const el of iterable){
if (el)) {
return true
}
}
return false
}

The range

Since 10^24 size array is huge and we won’t be able to actually have such an array (it’s bigger than 2^32), we ‘ll go ahead and make a generator just for demo purposes:

1
2
3
4
function* range(start:number, end:number,step: number= 1) {
let x = start - step;
while(x < end - step) yield x += step;
}

Putting it all together

After putting the above together we achieve the same results as with python build in functions.

1
2
3
4
5
6
7
8
9
10
11
12
function isEven(a:number){
console.log(`processing ${a}`)
return a%2 === 0
}

const hugeRange = range(1, Math.pow(10,24)+1);

if (some(map(hugeRange, isEven))){
console.log('Some even found')
}else{
console.log('No even found')
}

The above will have the following result:

1
2
3
processing 1
processing 2
Some even found

Playground Link

How about existing javascript libraries?

The only javascript library that I know of, giving similar behavior is rxjs , through it’s map and every (similar to python’s all) operators. Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { range } from 'rxjs/observable/range';
import { every, map } from 'rxjs/operators';

function isOdd(a: number) {
console.log(`processing ${a}`);
return a % 2 === 1;
}

range(1, Math.pow(10, 24) + 1)
.pipe(
map(isOdd),
every((x) => x)
)
.subscribe((allOdd) => {
if (!allOdd) {
console.log('Some even found');
} else {
console.log('All Odd');
}
});

The above will have the following result:

1
2
3
processing 1
processing 2
Some even found

Stackblitz

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2012-2023 Andreas Galazis

请我喝杯咖啡吧~