The central module that brings all of the separate parts of the library together into a public API. Everything publicly available is available through this module or one of its child modules.
All of the functions in this module deal directly with transducers. But first, let's talk about the protocols that are going to be referred to throughout many of the function discussions.
Protocols
One of the key selling points for transducers is that the same transducer can be used on any type of collection.
Rather than having to write a new map
function (for example) for every kind of collection - one for an array, one
for a string, one for an iterator, etc. - there is a single map
transducer that will work with all of them, and
potentially with any kind of collection. This is possible implementing protocols on the collections.
A protocol in JavaScript is much like an interface in languages like Java and C#. It is a commitment to providing a
certain functionality under a certain name. ES2015 has seen the introduction of an iterator
protocol, for example,
and language support for it (the new for...of
loop can work with any object that correctly implements the
iterator
protocol).
To support transduction, Xduce expects collections to implement four protocols.
iterator
: a function that returns an iterator (this one is built in to ES6 JavaScript)transducer/init
: a function that returns a new, empty instance of the output collectiontransducer/step
: a function that takes an accumulator (the result of the reduction so far) and the next input value, and then returns the accumulator with the next input value added to ittransducer/result
: a function that takes the reduced collection and returns the final output collection
iterator
is the built-in JavaScript protocol. When called, it is expected to return an iterator over the
implementing collection. This iterator is an object that has a next
function. Each call to next
is expected to
return an object with value
and done
properties, which respectively hold the next value of the iterator and a
boolean to indicate whether the iteration has reached its end. (This is a simplified explanation; see
this MDN page for more
detailed information.)
transducer/init
(referred to from now on as init
) should be a function that takes no parameters and returns a
new, empty instance of the output collection. This is the function that defines how to create a new collection of the
correct type.
transducer/step
(referred to from now on as step
) should be a function that takes two parameters. These
parameters are the result of the reduction so far (and so is a collection of the output type) and the next value from
the input collection. It must return the new reduction result, with the next value incorporated into it. This is the
function that defines how reduce a value onto the collection.
transducer/result
(referred to from now on as result
) should be a function that takes one parameter, which is the
fully reduced collection. It should return the final output collection. This affords a chance to make any last-minute
adjustments to the reduced collection before returning it.
Arrays, strings, and objects are all given support for all of these protocols. Other collections will have to provide
their own (though it should be noted that since iterator
is built-in, many third-party collections will already
implement this protocol). As an example, let's add transducer support to a third-party collection, the
Immutable.List
collection from immutable-js.
Immutable.List.prototype[protocols.init] = () => Immutable.List().asMutable();
Immutable.List.prototype[protocols.step] = (acc, input) => acc.push(input);
Immutable.List.prototype[protocols.result] = (value) => value.asImmutable();
Immutable.List
already implements iterator
, so we don't have to do it ourselves.
The init
function returns an empty mutable list. This is important for immutable-js because its default lists are
immutable, and immutable lists mean that a new list has to be created with every reduction step. It would work fine,
but it's quite inefficient.
The step
function adds the next value to the already-created list. Immutable.List
provides a push
function that
works like an array's push
, except that it returns the new list with the value pushed onto it. This is perfect for
our step
function.
The result
function converts the now-finished mutable list into an immutable one, which is what's going to be
expected if we're transducing something into an Immutable.List
. In most cases, result
doesn't have to do any
work, but since we're creating an intermediate representation of our collection type here, this lets us create the
collection that we actually want to output. (Without result
, we would have to use immutable lists all the way
through, creating a new one with each step
function, since we wouldn't be able to make this converstion at the
end.)
With those protocols implemented on the prototype, Immutable.List
collections can now support any transduction we
can offer.
Protocols
After talking a lot about protocols and showing how they're properties added to an object, it's probably pretty
obvious that there's been no mention of what the actual names of those properties are. That's what
protocols
is for.
protocols
means that the actual names aren't important, which is good because the
name might vary depending on whether or not the JavaScript environment has symbols defined. That unknown quantity can
be abstracted away by using the properties on the protocols
object as property keys.
(Besides, the actual name of the protocol will either be a Symbol
for the name of the protocol or a string like
'@@transducer/init'
, depending on whether Symbol
s are available, and those aren't a lot of fun to work with.)
The best way to use these keys can be seen in the immutable-js example above. Instead of worrying about the name of
the key for the init
protocol, the value of protocols.init
is used.
protocols
defines these protocol property names.
iterator
: if this is built in (like in later versions of node.js or in ES2015), this will match the built-in protocol name.init
step
result
reduced
: used internally to mark a collection as already reducedvalue
: used internally to provide the actual value of a reduced collection
The final two values don't have a lot of use outside the library unless you're writing your own transducers.
How Objects Are Treated
Before getting onto the core functions, let's talk about objects.
Objects bear some thought because regularly, they aren't candidates for iteration. They don't have any inherent order, normally something that's necessary for true iteration, and they have two pieces of data (key and value) for every element instead of one. Yet it's undeniable that at least for most transformations, being able to apply them to objects would be quite handy.
For that reason, special support is provided end-to-end for objects.
Object iteration
Iterating over an object will produce one object per property of the original object. An order is imposed; by
default, this order is "alphabetical by key". The iterator
function can be passed a
sorting function that can sort keys in any other way.
The result of the iteration, by default, is a set of objects of the form {k: key, v: value}
, called kv-form. The
reason for this form is that it's much easier to write transformation functions when you know the name of the key. In
the regular single-property {key: value}
form (which is still available by passing false
as the third parameter
to iterator
), the name of the key is unknown; in kv-form, the names of the keys are
k
and v
.
var obj = {c: 1, a: 2, b: 3};
var reverseSort = function (a, b) { return a < b ? 1 : b > a ? -1 : 0; };
var result = iterator(obj);
// asArray(result) = [{k: 'a', v: 2}, {k: 'b', v: 3}, {k: 'c', v: 1}]
result = iterator(obj, reverseSort);
// asArray(result) = [{k: 'c', v: 1}, {k: 'b', v: 3}, {k: 'a', v: 2}]
result = iterator(obj, null, false);
// asArray(result) = [{a: 2}, {b: 3}, {c: 1}]
result = iterator(obj, reverseSort, false);
// asArray(result) = [{c: 1}, {b: 3}, {a: 2}]
Internally, every object is iterated into kv-form, so if you wish to have it in single-property, you must use
iterator
in this way and pass that iterator into the transduction function.
Object transformation
The kv-form makes writing transformation functions a lot easier. For comparison, here's what a mapping function (for
a map
transformer) would look like if we were using the single-property form.
function doObjectSingle(obj) {
var key = Object.keys(obj)[0];
var result = {};
result[key.toUpperCase()] = obj[key] + 1;
return result;
}
Here's what the same function looks like using kv-form.
function doObjectKv(obj) {
var result = {};
result[obj.k.toUpperCase()]: obj.v + 1;
return result;
}
This is easier, but we can do better. The built-in reducers also recognize kv-form, which means that we can have our mapping function produce kv-form objects as well.
function doObjectKvImproved(obj) {
return {k: obj.k.toUpperCase(), v: obj.v + 1};
}
This is clearly the easiest to read and write - if you're using ES5. If you're using ES2015, destructuring and
dynamic object keys allow you to write doObjectKv
as
doObjectKv = ({k, v}) => {[k.toUpperCase()]: v + 1};
Reducing objects
The built-in reducers (for arrays, objects, strings, and iterators) understand kv-form and will reduce objects properly whether they're in single-property or kv-form. If you're adding transducer support for non-supported types, you will have to decide whether to support kv-form. It takes a little extra coding, while single-property form just works.
That's it for object-object reduction. Converting between objects and other types is another matter.
Every transducer function except for sequence
is capable of turning an object into a
different type of collection, turning a different type of collection into an object, or both. Objects are different
because they're the only "collections" that have two different pieces of data per element. Because of this, we have
to have a strategy on how to move from one to another.
Transducing an object into a different type is generally pretty easy. If an object is converted into an array, for instance, the array elements will each be single-property objects, one per property of the original object.
Strings are a different story, since encoding a single-property object to a string isn't possible (because every "element" of a string has to be a single character). Strings that are produced from objects will instead just be the object values, concatenated. Because objects are iterated in a particular order, this conversion will always produce the same string, but except in some very specific cases there really isn't a lot of use for this converstion.
var obj = {a: 1, b: 2};
var result = asArray(obj);
// result = [{a: 1}, {b: 2}]
result = asIterator(obj);
// result is an iterator with two values: {a: 1} and {b: 2}
result = into(Immutable.List(), obj)
// result is an immutable list with two elements: {a: 1} and {b: 2}
result = asString(obj);
// result is '12'
The opposite conversion depends on the values inside the collections. If those values are objects, then the result is
an object with all of the objects combined (if more than one has the same key, the last one is the one that's kept).
Otherwise, keys are created for each of the elements, starting with 0
and increasing from there.
This means that converting an object to any non-string collection and back produces the original object.
var result = asObject([{a: 1}, {b: 2}]);
// result = {a: 1, b: 2}
result = asObject([1, 2, 3]);
// result = {0: 1, 1: 2, 2: 3}
result = asObject('hello');
// result = {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}
Namespaces
Members
(static, constant) protocols :module:xduce~protocolMap
- Source:
The mapping of protocol names to their respective property key names.
The values of this map will depend on whether symbols are available, whatever is present here will be used as key names for protocol properties throughout the library.
Type:
Methods
(static) asArray(collection, xformopt) → {array}
- Source:
Transforms the elements of the input collection and reduces them into a new array.
The transformer is optional. If it isn't present, this function just converts the input collection into an array.
const xform = map(x => x + 1);
let result = asArray([1, 2, 3, 4, 5], xform);
// result = [2, 3, 4, 5, 6]
result = asArray('12345', xform);
// result = [2, 3, 4, 5, 6]
result = asArray('12345');
// result = [1, 2, 3, 4, 5]
result = asArray({a: 1, b: 2});
// result = [{a: 1}, {b: 2}]
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
|
xform |
module:xduce~transducerFunction |
<optional> |
A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. If this isn't present, the input collection will simply be reduced into an array without transformation. |
Returns:
An array containing all of the transformed values from the input collection elements.
- Type
- array
(static) asIterator(collection, xformopt) → {module:xduce~iterator}
- Source:
Transforms the elements of the input collection and reduces them into a new iterator.
The transformer is optional. If it isn't present, this function just converts the input collection into an iterator.
(The results here are shown passed through asArray
because there's no literal interpretation of an iterator to
show. The asArray
calls are for demonstration purposes only.)
const xform = map(x => x + 1);
function* five() {
for (let i = 1; i <= 5; ++i) {
yield i;
}
};
let result = asIterator(five(), xform);
// asArray(result) = [2, 3, 4, 5, 6]
result = asIterator([1, 2, 3, 4, 5], xform);
// asArray(result) = [2, 3, 4, 5, 6]
result = asIterator([1, 2, 3, 4, 5]);
// asArray(result) = [1, 2, 3, 4, 5]
result = asIterator({a: 1, b: 2});
// asArray(result) = [{a: 1}, {b: 2}]
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
|
xform |
module:xduce~transducerFunction |
<optional> |
A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. If this isn't present, the input collection will simply be reduced into an iterator without transformation. |
Returns:
An iterator containing all of the transformed values from the input collection elements.
(static) asObject(collection, xformopt) → {object}
- Source:
Transforms the elements of the input collection and reduces them into a new object.
The transformer is optional. If it isn't present, this function just converts the input collection into an object.
const xform = map(({ k, v }) => ({ [k]: v + 1 }));
let result = asObject({a: 1, b: 2}, xform);
// result = {a: 2, b: 3}
result = asObject([{a: 1}, {b: 2}], xform);
// result = {a: 2, b: 3}
result = asObject([1, 2, 3, 4, 5]);
// result = {0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
result = asObject('hello');
// result = {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
|
xform |
module:xduce~tranducerFunction |
<optional> |
A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. If this isn't present, the input collection will simply be reduced into an object without transformation. |
Returns:
An object containing all of the transformed values from the input collection elements.
- Type
- object
(static) asString(collection, xformopt) → {string}
- Source:
Transforms the elements of the input collection and reduces them into a new string.
The transformer is optional. If it isn't present, this function just converts the input collection into an string.
const xform = map(x => x.toUpperCase());
let result = asString('hello', xform);
// result = 'HELLO'
result = asString(['h', 'e', 'l', 'l', 'o'], xform);
// result = 'HELLO'
result = asString([1, 2, 3, 4, 5]);
// result = '12345'
result = asString({a: 1, b: 2});
// result = '12'
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
|
xform |
module:xduce~transducerFunction |
<optional> |
A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. If this isn't present, the input collection will simply be reduced into a string without transformation. |
Returns:
A string containing all of the transformed values from the input collection elements.
- Type
- string
(static) compose(…fns) → {module:xduce~transducerFunction}
- Source:
Composes two or more transducer functions into a single transducer function.
Each function that takes a transducer function (sequence
,
into
, etc.) is only capable of accepting one of them. If there is a need to have
several transducers chained together, then use compose
to create a transducer function that does what all of them
do.
This operates only on transducer functions, not on
transducers themselves. There is no option for a shortcut form on a composed
transducer function. They must be passed to functions that operate on them (sequence
and the like).
NOTE: In functional programming, a compose function is generally ordered so that the first-executed function is listed last. This is done in the opposite way, with the first transducer executing first, etc. This is more sensible for transducer functions.
const add1 = x => x + 1;
const odd = x => x % 2 !== 0;
const xform = compose(map(add1), filter(odd), take(3));
const result = sequence([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], xform);
// result = [3, 5, 7];
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
fns |
module:xduce~transducerFunction |
<repeatable> |
One or more function that each create a transducer object that defines the transformation being done to a collection's elements. Any of the transducers in this library can produce a suitable transducer function. |
Returns:
A transducer function that produces a transducer object that performs all of the transformations of the objects produced by the input transducer functions.
(static) into(target, collection, xformopt) → {*}
- Source:
Transforms the elements of the input collection and reduces them into the target collection.
This is much like transduce
, except that instead of explicitly providing a reducer
(and perhaps an initial collection), the target collection acts as a reducer itself. This requires that the
collection implement the init
, result
, and step
transducer protocol functions.
If no transducer function is provided, the input collection elements are reduced into the target collection without being transformed. This can be used to convert one kind of collection into another.
const xform = map(x => x + 1);
let result = into([], [1, 2, 3, 4, 5], xform);
// result = [2, 3, 4, 5, 6]
result = into('', [1, 2, 3, 4, 5], xform);
// result = '23456'
result = into([], '12345', xform);
// result = [2, 3, 4, 5, 6]
result = into('', '12345', xform);
// result = '23456'
These examples are exactly equivalent to the four examples under transduce
, but
using into
instead.
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
target |
* | The collection into which all of the transformed input collection elements will be reduced. This
collection must implement the |
|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
|
xform |
module:xduce~transducerFunction |
<optional> |
A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. If this isn't present, the input collection will simply be reduced into the target collection without transformation. |
Returns:
The target
collection, with all of the tranformed input collection elements reduced onto it.
- Type
- *
(static) iterator(obj, sortopt, kvopt) → {module:xduce~iterator}
- Source:
Creates an iterator over the provided collection.
For collections that are not objects, it's as simple as that. Pass in the collection, get an iterator over that collection.
const iter = iterator([1, 2, 3]);
iter.next().value === 1; // true
iter.next().value === 2; // true
iter.next().value === 3; // true
iter.next().done === true; // true
Objects get special support though, as noted in the section above on iterating over objects. Objects are iterated in
alphabetical order by key, unless a second parameter is passed to iterator
. This must be a function that takes two
parameters (which will be object keys) and returns -1
if the first is less than the second, 1
if the second is
less than the first, and 0
if they're equal.
Also, iterator
by default iterates objects into key/value form. However, if a third parameter of true
is passed,
it will instead render the object in kv-form. This is the form used internally when a transducer is invoked.
The second and third parameters are ignored if the input collection is not an object.
const iter = iterator({ b: 2, a: 4 });
iter.next().value.a === 4; // true
iter.next().value.b === 2; // true
iter.next().done === true; // true
const kvIter = iterator({ b: 2, a: 4 }, null, true);
const { k: k1, v: v1 } = kvIter.next().value;
k1 === 'a' && v1 === 4; // true
const { k: k2, v: v2 } = kvIter.next().value;
k2 === 'b' && v2 === 2; // true
iter.next().done === true; // true
Note that if this function is passed an object that looks like an iterator (an object that has a next
function),
the object itself is returned. It is assumed that a function called next
conforms to the iteration protocol.
If this function is provided a function as its first argument, an iterator is returned which runs that function
one time for each call to next
. That function is provided two arguments: the index of the call (starting at 0
for the first time it's called and increasing by 1 per invocation after that) and the return value of the previous
call to the function (starting at undefined
for the first run before the function is ever called). If the function
ever returns undefined
, the iterator will terminate and set the done
property of its return value to true
at
that point.
Note that since the initial value of the second argument is undefined
, using default arguments is an excellent way
of providing the function an initial value.
const constIter = iterator(() => 6); // Bert's favorite number
constIter.next().value === 6; // true
constIter.next().value === 6; // true;
// This will go on forever, as long as `next` keeps getting called
const indexIter = iterator(x => x * x);
indexIter.next().value === 0; // true
indexIter.next().value === 1; // true
indexIter.next().value === 4; // true
indexIter.next().value === 9; // true
// Again, this will go on forever, or until the numbers get to big JS to handle
// Using default value on `last` parameter for initial value
const lastIter = iterator((index, last = 1) => last * (index + 1)); // Factorial
lastIter.next().value === 1; // true
lastIter.next().value === 2; // true
lastIter.next().value === 6; // true
lastIter.next().value === 24; // true
// Again, forever, though factorials get big quickly
// This iterator will terminate when the function returns `undefined`
const stopIter = iterator(x => x < 2 ? x : undefined);
stopIter.next().value === 0; // true
stopIter.next().value === 1; // true
stopIter.next().done === true; // true
Parameters:
Name | Type | Attributes | Default | Description |
---|---|---|---|---|
obj |
* | The value to be iterated over. |
||
sort |
module:xduce~sort |
<optional> |
A function used to determine the sorting of keys for an object iterator. It has no effect when iterating over anything that is not a plain object. |
|
kv |
boolean |
<optional> |
false
|
Whether an object should be iterated into kv-form. This is only relevant when iterating over an object; otherwise its value is ignored. |
Returns:
An iterator over the provided value. If the value is not iterable (it's not an
array, object, or string, and it doesn't have a protocol-defined iterator), null
is returned.
(static) reduce(collection, reducer, init) → {*}
- Source:
Reduces the elements of the input collection through a reducer into an output collection.
This is the lowest-level of the transduction functions. In fact, this one is so low-level that it doesn't have a lot of use in normal operation. It's more useful for writing your own transformation functions.
reduce
doesn't assume that there's even a transformation. It requires an initial collection and a reducer object
that is matched to that initial collection. The reducer object must implement the step
and result
protocols,
which instruct reduce
on how to build up the collection. The reducer may implement a transformation as well, but
all that's important here is that it can do the reduction.
The input collection need only implement iterator
. It is not necessary for the input and output collections to be
of the same type; as long as the input implements iterator
and the reducer implements step
and result
appropriate to the type of the init
collection, then any translation between collection types can occur.
The normal course of operation will be to call transduce instead, as that function makes it easy to combine transformations with reductions and can optionally figure out the initial collection itself.
Parameters:
Name | Type | Description |
---|---|---|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
reducer |
object | An object that implements the |
init |
* | a collection of the same type as the output collection. It need not be empty; if it is not, the existing elements are retained as the input collection is reduced into it. |
Returns:
A new collection, consisting of the init
collection with all of the elements of the collection
collection reduced into it.
- Type
- *
(static) sequence(collection, xform) → {*}
- Source:
Transforms the elements of the input collection and reduces them into a new collection of the same type.
This is the highest level of the three main transduction functions (sequence
, into
,
and transduce
). It creates a new collection of the same type as the input collection
and reduces the transformed elements into it. Additionally, unlike into
and
transduce
, this function is capable of producing an iterator (as long as the input
is an iterator).
The input collection must not only implement the iterator
protocol (as in the last two functions) but also the
init
, result
, and step
transducer protocols. Special support is provided for arrays, strings, objects, and
iterators, so they need not implement any protocol.
The obvious limitation of this function is that the type of output collection cannot be chosen. Since it is always the same as the input collection, this function cannot be used to convert a collection into a different type.
const xform = map(x => x + 1);
let result = sequence([1, 2, 3, 4, 5], xform);
// result = [2, 3, 4, 5, 6]
result = sequence('12345', xform);
// result = '23456'
These examples are identical to two of the four examples from the asX
functions. The other two examples are not
possible with sequence
because they have different input and output collection types.
Parameters:
Name | Type | Description |
---|---|---|
collection |
* | The input collection. This must implement the |
xform |
module:xduce~transducerFunction | A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. |
Returns:
A collection of the same type as the input collection, containing all of the transformed values from the input collection elements.
- Type
- *
(static) toFunction(xform, reducer) → {module:xduce~step}
- Source:
Creates a reduction function from a transducer and a reducer.
This produces a function that's suitable for being passed into other libraries' reduce functions, such as
JavaScript's Array.prototype.reduce
or Lodash's _.reduce
. It requires both a transformer and a reducer because
reduction functions for those libraries must know how to do both. The reducer can be a standard reducer object like
the ones sent totransduce
or reduce
, or it can be a
plain function that takes two parameters and returns the result of reducing the second parameter into the first.
If there is no need for a transformation, then pass in the identity
transducer.
Parameters:
Name | Type | Description |
---|---|---|
xform |
module:xduce~transducerObject | A transducer object whose step function will become the returned reduction function. |
reducer |
module:xduce~step | module:xduce~transducerObject | A reducer that knows how to reduce values into an
output collection. This can either be a reducing function or a transducer object whose |
Returns:
A function that handles both the transformation and the reduction of a value onto a target function.
- Type
- module:xduce~step
(static) toReducer(collection) → {object}
- Source:
Creates a reducer object from a function or from a built-in reducible type (array, object, or string).
To create a reducer for arrays, objects, or strings, simply pass an empty version of that collection to this function
(e.g., toReducer([])
). These reducers support the kv-form for objects.
The notable use for this function though is to turn a reduction function into a reducer object. The function is a
function oftwo parameters, an accumulator and a value, and returns the accumulator with the value in it. This is
exactly the same kind of function that is passed to reduction functions like JavaScript's Array.prototype.reduce
and Lodash's _.reduce
.
Note in particular that the output of this reducer does not need to be a collection. It can be anything. While transducing normally involves transforming one collection into another, it need not be so. For example, here is a reducer that will result in summing of the collection values.
const { toReducer, reduce } = xduce;
const sumReducer = toReducer((acc, input) => acc + input);
const sum = reduce([1, 2, 3, 4, 5], sumReducer, 0);
// sum = 15
This can be combined with transducers as well, as in this calculation of the sum of the squares of the collection values.
const { toReducer, transduce } = xduce;
const { map } = xduce.transducers;
const sumReducer = toReducer((acc, input) => acc + input);
const sum = transduce([1, 2, 3, 4, 5], map(x => x * x), sumReducer, 0);
// sum = 55
Parameters:
Name | Type | Description |
---|---|---|
collection |
* | An iterable collection or a reducer function. |
Returns:
An object containing protocol properties for init, step, and result. This object is suitable for
use as a reducer object (one provided to reduce
or transduce
).
If the provided collection is not iterable, all of the properties of this object will be null
.
- Type
- object
(static) transduce(collection, xform, reducer, initopt) → {*}
- Source:
Transforms the elements of the input collection and reduces them into an output collection.
This is the lowest-level of the transduction functions that is likely to see regular use. It does not assume anything about the reducer, as it asks for it to be passed explicitly. This means that any kind of collection can be produced, since the reducer is not tied to the input collection in any way.
transduce
also will accept an initial value for the resulting collection as the optional last parameter. If this
parameter isn't present, then the initial value is determined from the transducer init protocol property on the
reducer. Note however that many reducers may not provide an initial value, and in those cases it will have to be
passed as a parameter.
const xform = map(x => x + 1);
const arrayReducer = {
[protocols.init]() { return []; },
[protocols.result](x) { return x; },
[protocols.step](acc, x) {
acc.push(x);
return acc;
}
};
const stringReducer = {
[protocols.init]() { return ''; },
[protocols.result](x) { return x; },
[protocols.step](acc, x) { return acc + x; }
};
let result = transduce([1, 2, 3, 4, 5], xform, arrayReducer);
// result = [2, 3, 4, 5, 6]
result = transduce([1, 2, 3, 4, 5], xform, stringReducer);
// result = '23456'
result = transduce('12345', xform, arrayReducer);
// result = [2, 3, 4, 5, 6]
result = transduce('12345', xform, stringReducer);
// result = '23456'
These examples illustrate a number of important concepts. First of all, the transducer function is independent of the
type of the collection; the same transducer function is used no matter the type of input or output collections.
Secondly, two reducers are defined. These are objects that conform to the transducer protocol (see the documentation
on module:xduce.protocols
) and that know how to produce the output collection of choice. In this case, the
reducers know how to create new arrays and strings (the init
protocol) and how to add elements to arrays and
strings (the step
protocol). Because these reducers do have init
protocol properties, the transduce
calls do
not require explicit initial collections.
The final point is that transduce
can accept any kind of iterable collection, and by passing in the proper reducer,
it can produce any kind of output collection. The same transduce
function and the same map transformer is used in
all four examples, despite the input/output combination being different in all four.
The init
collection doesn't have to be empty. If it isn't, the elements that are already in it are retained and the
transformed input elements are reduced into the collection normally.
Of course, the examples are not really necessary - the same thing could be accomplished using
into
without having to create the reducers (strings and arrays passed to
into
as targets know how to reduce themselves already).
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
collection |
* | The input collection. The only requirement of this collection is that it implement the
|
|
xform |
module:xduce~transducerFunction | A function that creates a transducer object that defines the transformation being done to the input collection's elements. Any of the transducers in this library can produce a suitable transducer function. |
|
reducer |
object | An object that implements the transducer protocols ( |
|
init |
* |
<optional> |
aAcollection of the same type as the output collection. If this is not present, then the reducer's
|
Returns:
A collection of a type determined by the passed reducer. The elements of this collection are the results from the transformer function being applied to each element of the input collection.
- Type
- *
Type Definitions
init() → {*}
Returns a new, blank, empty instance of the target collection.
Returns:
An initial instance of the target collection. This is usually, but is not required to be, an empty instance of the collection.``
- Type
- *
iterator
Properties:
Name | Type | Description |
---|---|---|
next |
module:xduce~next | A function that, when called, returns the next iterator value. |
A generic iterator. This conforms to the iterator
protocol in that it has a next
function that produces iterator
-compatible objects.
Type:
- object
next() → {module:xduce~nextValue}
The function that makes an object an iterator. This can be called repeatedly, with each call returning one iterator value, in order. This function must therefore keep state to know which of the values is the one to return next, based on the values that have already been returned by prior calls.
Returns:
An object containing the status and value of the next step of the iteration.
nextValue
Properties:
Name | Type | Attributes | Description |
---|---|---|---|
done |
boolean | A flag to indicate whether there are any more values remaining in the iterator. Once this
becomes |
|
value |
* |
<optional> |
The value returned by the iterator on this step. As long as |
An object returned by an iterator's next
function. It has two properties so one can be used as a flag, since
values like false
and undefined
can be legitimate iterator values.
Type:
- object
protocolMap
Properties:
Name | Type | Description |
---|---|---|
init |
string | Symbol | The |
init |
string | Symbol | The |
step |
string | Symbol | The |
result |
string | Symbol | The |
reduced |
string | Symbol | The |
value |
string | Symbol | The |
The mapping of protocol names to their respective property key names. The values of this map will depend on whether symbols are available.
Type:
- object
result(input) → {*}
Performs any post-processing on the completely reduced target collection. This lets a transducer make a final, whole-collection transformation, particularly useful when the step function has been used on an intermediate form of the collection which is not meant to be the output.
Parameters:
Name | Type | Description |
---|---|---|
input |
* | The final, reduced collection derived from using module:xduce~step on each of the original collection's elements. |
Returns:
The final collection that is the result of the transduction.
- Type
- *
sort(a, b) → {number}
A function used for sorting a collection.
Parameters:
Name | Type | Description |
---|---|---|
a |
* | The first item to compare. |
b |
* | The second item to compare. |
Returns:
Either 1
if a
is less than b
, -1
if a
is greater than b
, or 0
if a
is equal to b
.
- Type
- number
step(acc, value) → {*}
Performs a transformation on a single element of a collection, adding it to the target collection at the end. Thus, this function performs both the transformation and the reduction steps.
Parameters:
Name | Type | Description |
---|---|---|
acc |
* | The target collection into which the transformed value will be reduced. |
value |
* | A single element from the original collection, which is to be tranformed and reduced into the target collection. |
Returns:
The resulting collection after the provided value is reduced into the target collection.
- Type
- *
transducer(collectionopt, …params) → {*|module:xduce~transducerFunction}
A function that is responsible for performing transductions on collections.
These functions have two forms. If no input collection is supplied, then this takes a set of configuration parameters and returns a transducer function configured to handle that specific transformation.
There is also a shortcut form, where an input collection is supplied. In this case, a transducer function is still
configured and created, but then it is immediately applied as though sequence
was
called with that collection and transducer function. The transformed collection is then returned.
Parameters:
Name | Type | Attributes | Description |
---|---|---|---|
collection |
* |
<optional> |
An optional input collection that is to be transduced. |
params |
* |
<repeatable> |
Parameters that are used to configure the underlying transformation. Which parameters are necessary depends on the transducer. See the individual transducers for details. |
Returns:
If a collection is supplied, then the function returns a new
collection of the same type with all of the elements of the input collection transformed. If no collection is
supplied, a transducer function, suitable for passing to sequence
,
into
, etc. is returned.
- Type
- * | module:xduce~transducerFunction
transducerFunction(xform) → {module:xduce~transducerObject}
A function that creates a transducer object and links it to the next one in the chain.
Parameters:
Name | Type | Description |
---|---|---|
xform |
module:xduce~transducerObject | A transducer object to chain the new transducer object to. |
Returns:
A new transducer object already chained to xform
.
transducerObject
Properties:
Name | Type | Description |
---|---|---|
@@transducer/init |
module:xduce~init | An implementation of the transducer |
@@transducer/step |
module:xduce~step | An implementation of the transducer |
@@transducer/result |
module:xduce~result | An implementation of the transducer |
An object implementing all three transduction protocols (init
, step
, and result
) which is used by the engine
to define transduction.
Transducer objects can (and must) be chained together. For instance, none of the transducer functions defined in
module:xduce.transducers produces objects that know how to reduce transformed values into an output
collection. This is the entire point; reduction is separate from transformation, which allows transformations to be
used no matter the type of the output collection. So the engine automatically chains transducer objects to a reducer
object (which is basically a specialized transducer objects whose step
function transforms its inputs by adding
them to a collection) that does know how to create an output collection. Thus, the protocol methods need to call
the protocol methods of the next transducer object in the chain.
For that reason, transducer objects are not created manually. They are instead created by transducer functions that automatically create transducer objects and link them to the next transducer object in the chain.
Type:
- object