In Java 8 using streams when I chain methods one after the another the execution of operations are performed in pipelined manner.
Example:
List<Integer> nums = Arrays.asList(1,2,3,4,5,6);
nums.stream().map(x->{
x = x * x;
System.out.println("map1="+x);
return x;
}).map(x->{
x = x * 3;
System.out.println("map2="+x);
return x;
}).forEach(x-> System.out.println("forEach="+x));
Output:-
map1=1
map2=3
forEach=3
map1=4
map2=12
forEach=12
map1=9
map2=27
forEach=27
map1=16
map2=48
forEach=48
map1=25
map2=75
forEach=75
map1=36
map2=108
forEach=108
But when I tried similarly in javascript.The result is different.As in javascript first operation gets completed and then second operation is performed.Example:-
var nums = [1,2,3,4,5,6 ];
nums.map(x => {
x = (x * x);
console.log('map1='+x);
return x;})
.map(x => {
x = x * 3;
console.log('map2='+x);
return x;})
.forEach(x=> console.log('forEach='+x));
Output:-
map1=1
map1=4
map1=9
map1=16
map1=25
map1=36
map2=3
map2=12
map2=27
map2=48
map2=75
map2=108
forEach=3
forEach=12
forEach=27
forEach=48
forEach=75
forEach=108
Is there any way in JavaScript to make it performs operations in a pipeline manner, and I get output as in the Java program?
This question ask only how to collect like in JavaScript but not how internal working changes for same type of methods.
Create stream from an array:of() and Arrays. stream() are two commonly used methods for creating a sequential stream from a specified array. Both these methods returns a Stream when called with a non-primitive type T.
The Stream API gives you two terminal operations to find an element: findFirst() and findAny() . These two methods do not take any argument and return a single element of your stream. To properly handle the case of empty streams, this element is wrapped in an optional object.
A stream can be composed of multiple functions that create a pipeline that data that flows through. This data cannot be mutated. That is to say the original data structure doesn't change. However the data can be transformed and later stored in another data structure or perhaps consumed by another operation.
Maybe later (or never) you can use the actual experimental pipeline operator |>
, which has the following syntax:
expression |> function
Your wanted result could be achieved by taking the functions as separate functions and iterate the stream array for each pipe.
This works only in FF. From version 58: this feature is behind the --enable-pipeline-operator
compile flag.
const
a = x => { x = x * x; console.log("map1=" + x); return x; },
b = x => { x = x * 3; console.log("map2=" + x); return x; },
c = x => console.log("forEach=" + x)
var nums = [1, 2, 3, 4, 5, 6];
nums.forEach(v => v |> a |> b |> c);
The same with a pipe as function (function composition enabling piping) with a closure over the wanted functions.
const
pipe = (...functions) => input => functions.reduce((acc, fn) => fn(acc), input),
a = x => { x = x * x; console.log("map1=" + x); return x; },
b = x => { x = x * 3; console.log("map2=" + x); return x; },
c = x => console.log("forEach=" + x)
var nums = [1, 2, 3, 4, 5, 6],
pipeline = pipe(a, b, c);
nums.forEach(pipeline);
If you put each function operation into an array, you can iterate over that array with reduce
and pass the last calculated value along in the accumulator until the end of the function array is reached:
var nums = [1,2,3,4,5,6 ];
var fns = [
(x) => {
x = x * x;
console.log('map1=' + x);
return x;
},
(x) => {
x *= 3;
console.log('map2=' + x);
return x;
},
(x) => {
console.log(x);
return x;
}
];
nums.forEach((num) => {
fns.reduce((lastResult, fn) => fn(lastResult), num);
// pass "num" as the initial value for "lastResult",
// before the first function has been called
});
You can't use nums.map
because .map
will necessarily iterate through the whole input array before resolving to the mapped output array (after which the mapped output array will then have another .map
called on it).
The equivalent to Java's streams are JavaScript's iterators. Iterator objects unfortunately don't have a map
method (yet), but you can easily write one yourself (and even install it on the prototype if you want method syntax).
function* map(iterable, f) {
for (var x of iterable)
yield f(x);
}
var nums = [1,2,3,4,5,6];
function square(x) {
x = (x * x);
console.log('map1='+x);
return x;
}
function triple(x) {
x = x * 3;
console.log('map2='+x);
return x;
}
for (const x of map(map(nums.values(), square), triple)) {
console.log('forEach='+x);
}
Also notice that in functional programming, order doesn't matter for pure operations - you shouldn't need to rely on the execution order if you are using map
.
Why re-invent from scratch when we have solutions. This functionality is present in lodash/RxJS/stream.js.
Example snippet from lodash:
_.flow(
_.assign(rows[0]),
_.omit('blah')
)(foundUser);
// >> {"charData":[],"ok": 1}
However, javascript runs on single thread and so do these libraries. Java streams benefit from multi core systems(in case of parallel). There they can use multiple threads to use all available cores.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With