Array#map
, Array#filter
create a new array and hence effectively iterating over the array (or creating new array).
Whereas in rust, python, java, c#, etc. such expression chain will iterate only once.
In most cases this is irrelevant and we do not have to care about that. However in some cases the performance hit could be a deal breaker to leverage the function api of the Array
class.
How do you mitigate this? So you have any preference on a library enabling lazy evaluation for functional expression?
Array#map
, Array#filter
create a new array and hence effectively iterating over the array (or creating new array).
Whereas in rust, python, java, c#, etc. such expression chain will iterate only once.
In most cases this is irrelevant and we do not have to care about that. However in some cases the performance hit could be a deal breaker to leverage the function api of the Array
class.
How do you mitigate this? So you have any preference on a library enabling lazy evaluation for functional expression?
Share Improve this question edited May 17, 2021 at 20:27 Nas asked May 17, 2021 at 15:46 NasNas 1,1313 gold badges11 silver badges23 bronze badges 6- 4 Write it out as a loop, or use iterator methods. – Bergi Commented May 17, 2021 at 15:50
-
What if
reduce
is chained further with amap
? How can that in general work without first performing the whole iteration needed to perform the reduction? – trincot Commented May 17, 2021 at 16:55 - Duplicate of stackoverflow./questions/57042520/… – loop Commented Oct 14, 2021 at 20:25
-
npmjs./package/stream-list does exactly this.
npm i stream-list
– ohrlando Commented Aug 16, 2023 at 18:45 - iter-ops is one such library, and there are many others today. – vitaly-t Commented Aug 5, 2024 at 16:58
5 Answers
Reset to default 3If you don't want it to iterate more than once. You can use a loop
const numbers = [1,2,3,4,5,6]
let result = 0;
for(const number of numbers) {
const square = number * number
if(square % 2) {
result += square
}
}
console.log(result)
Or reduce
const numbers = [1,2,3,4,5,6]
const result = numbers.reduce((acc, number) => {
const square = number * number
if(square % 2) {
return acc + square
}
return acc
}, 0)
console.log(result)
Array methods aren't functional so the whole premise is flawed. The fact that they exist on the array object means they aren't open position the way pure functions are. You can get closer though
const square = (n) => n * n
const oddNumberOrZero = (n) => n % 2 ? n : 0
const add = (a, b) => a + b
const addOddSquare = (a, b) => add(a, oddNumberOrZero(square(b)))
const reduce = (arr, fn, acc) => arr.reduce(fn,acc)
const numbers = [1,2,3,4,5,6]
const result = reduce(numbers, addOddSquare, 0)
console.log(result)
You also seem to be conflating fluent interfaces with functional programming.
As of ECMAScript 2025 you can use iterator helpers.
Example:
const res = Array(100000000)
.keys() // 0, 1, 2, 3, 4, 5, 6, 7, ...
.map(x => 3*x) // 0, 3, 6, 9, 12, 15, 18, 21, ...
.filter(x => x % 5 == 1) // 6, 21, 36, 51, 66, ...
.take(4) // 6, 21, 36, 51
.reduce((a, b) => a + b) // Sum: 114
console.log(res);
This has the lazy behaviour: even though it looks like it, there is at no time an array with 100000000 elements. The initial array is sparse, and just has a length property and no entries. It is also the only array in this expression. The keys
method returns an iterator, which only gets consumed indirectly and lazily by the chain of iterator functions. It is the final reduce
call (which is also an iterator method) that initiates the consumption of values from the chained iterators (the "pipe"). As take(4)
limits the need for values, only a limited number of values are consumed from the keys()
iterator and those that follow it, so that this expression finishes quickly with a result.
I believe what you are looking for is processing the array as a stream. You can do that with highland:
import _ from "highland";
_([1, 2, 3, 4])
.filter(v => v % 2 === 0)
.map(v => v * 2)
.toArray((result: number[]) => {
// result is the new array
});
Relevant part from the docs:
Arrays - Streams created from Arrays will emit each value of the Array (...)
You can install stream-list lib from npm
https://www.npmjs./package/stream-list
let myNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let mylist = new List(myNumbers);
myList
.filter((num) => num % 2 == 0) //only pair
.map((num) => num * num) // square
.map((num) => num/2) // half
.toList(); // myNumbers was just itered once
Please check the following code lines. In the code, in which v * v is divided 2 means that v is divided.
const numbers = [1,2,3,4,5,6];
const res = numbers.reduce((sum, v)=> sum + (v % 2? v * v: 0), 0);
console.log(res)