I'm trying to learn transducers via egghead and I think I got it until we try to compose object transformation. I have an example below that doesn't work
const flip = map(([k,v]) => ({[v]: k}));
const double = map(([k,v]) => ({[k]: v + v}));
seq(flip, {one: 1, two: 2}); /*?*/ {1: 'one', 2: 'two'}
seq(double, {one: 1, two: 2}); /*?*/ {'one': 2, 'two: 4}
but if I compose it fails:
seq(compose(flip, double), {one: 1, two: 2}); /*?*/ {undefined: NaN}
seq(compose(double, flip), {one: 1, two: 2}); /*?*/ {undefined: undefined}
How can I work with objects using transducers with fp composition?
There is quite a bit of boiler plate so I really suggest looking at the live code example to review the utils like compose, seq etc.
first off thanks for going through the course. You're having trouble composing because we've got clashing data types between the expected inputs and outputs.
When composing flip and double, the seq
helper calls the transduce
helper function which will convert your input object to an array of [k,v]
entries so that it can iterate through it. It also calls your composed transform with the objectReducer
helper to be used as the inner reducer, which just does an Object.assign
to keep building up the accumulation.
It then iterates through the [k,v]
entries, passing them to your composed reducer, but it's up to you to ensure you keep the data types compatible between your transforms.
In your example, double
will get the return value of flip
, but double
expects a [k,v]
array, and flip returns an object.
So you would have to do something like this:
const entriesToObject = map(([k,v]) => {
return {[k]:v};
});
const flipAndDouble = compose(
map(([k,v]) => {
return [k,v+v];
}),
map(([k,v]) => {
return [v,k];
}),
entriesToObject,
);
//{ '2': 'one', '4': 'two', '6': 'three' }
It's a bit confusing since you have to ensure the last step returns an object and not a [k,v]
array. This is so the objReducer
that performs the Object.assign
will work correctly since it expects an object as the value.
This is why I added in entriesToObject
above.
If the objReducer
was updated to handle [k,v]
arrays as well as objects
as values then you could keep returning [k,v]
arrays from your last step as well, which is a much better approach
You can see an example of how the objReducer could be rewritten here: https://github.com/jlongster/transducers.js/blob/master/transducers.js#L766
For production use, if you use that transducer library, you can just keep treating your inputs and outputs as [k,v] arrays, which is a much better approach. For your own learning, you could try modifying the objReducer
based on that link and you should then be able to remove entriesToObject
from the composition above.
Hope that helps!