Reputation: 1121
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?
Upvotes: 6
Views: 2658
Reputation: 350137
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.
Upvotes: 2
Reputation: 136
You can install stream-list lib from npm
https://www.npmjs.com/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
Upvotes: 0
Reputation: 183
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 (...)
Upvotes: 1
Reputation: 3885
If 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 composition 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.
Upvotes: 2
Reputation: 505
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)
Upvotes: -2