transduce::(Function transformer, Function reducer, Any seed, Collection collection)=> Any output
description
Returns an output value after reducing an input collection using the provided transformer and reducer.
The reducer function should have the following type signature:
(Any accumulator, Any value, Any key, Collection collection) => Any output, where:
accumulator is the result of the previous reduce iterations
value is the value of the current item in the iteration
key is the key of the current item in the iteration
collection is the input collection being iterated on
The transformer function should accept a reducer and return another reducer function, and thus have the following type signature
(Any accumulator, Any value, Any key, Collection collection) =>
(Any accumulator, Any value, Any key, Collection collection) => Any output
Calling transduce(transformer, reducer, seed, collection) is equivalent to calling reduce(transformer(reducer), seed, collection).
transduce is basically equivalent to reduce with a separation of concerns between the transforming operation and the reducing one (hence the name trans-duce), favoring clarity and code reuse. Although it is very similar to reduce, it can be hard to grasp how it works and its usefulness. If you're not familiar with this concept, I highly suggest you read the excellent article What's a Transducer? by Reginald Raganwald Braithwaite.
Suppose we have an array of characters, each having a name and a side property. We want to get a new array containing only the names of the good guys. If we look closely at what we want to achieve, we can decompose it into 2 parts:
Transforming each item (filtering it and accessing a property)
Combining these items in a new array
Of course, we could also use map and filter to perform this operation, like so:
But that would also mean the characters array would be iterated over twice, which seems a pity.
Using transduce ensures the iteration is only done once, no matter how many transformations are done on each item.
concatNames is our reducer, simply adding a name to our array accumulator and returning it.
retrieveGoodGuysName is our transformer: it accepts a reducer and returns another one, but decorated with the actual transformation we want to perform
seed is simply an empty array
During the transduce operation, concatNamesis decorated by retrieveGoodGuysName and then our characters array is iterated on, and each item goes through the transforming operation before the reducer gets called. In the end, we get ['Luke', 'Han']. Hooray!
optimizing the previous example with utility transformers
Let's get back to our previous example, and especially this part where I said
Suppose we have an array of characters, each having a name and a side property. We want to get a new array containing only the names of the good guys. If we look closely at what we want to achieve, we can decompose it into 2 parts:
Transforming each item (filtering it and accessing a property)
Combining these items in a new array
Well, if you look at it even more closely, we can decompose (again!) the transforming part into 2 subparts:
Filtering (removing bad guys in our case)
Mapping (mapping an input value to an output)
Any transformation on a collection is actually a more specific version of reduce, and if you think of it, any transformation can be expressed as a combination of one or several mapping/transforming operations.
So conductor also features two utility transformer functions, which can be very useful in the context of transducers:
transformers/filter
transformers/map
You can write any data transformation as a combination of those two functions. Let's get back at our example.
Let's re-define our retrieveGoodGuysName as a combination of filter and map:
The filtering operation, isGood, will be done using transformers/filter to check if the item's side property is equal to light. The mapping operation, getName, will be done using transformers/map and simply consists of retrieving the name property.
Notice how these 2 utility transformer functions are not imported directly from conductor but rather from conductor/transformers. Also, since transformers/filter and transformers/map are higher order functions, they compose in a counterintuitive fashion.