import {
List
} from 'immutable';
import {
isLeaf,
asList
} from './utils';
/**
* @module reduce
*/
const reduceRecursive = (reduction, self, childPath, reducer, tree, options, nodePath = List()) => {
if(isLeaf(self, childPath)) {
return options.onlyParents ? reduction : reducer(reduction, self, nodePath, tree);
}
const reducedChildren = self
.getIn(childPath)
.reduce((red, kid, key) => reduceRecursive(red, kid, childPath, reducer, tree, options, nodePath.push(key)), reduction);
return options.onlyLeaves
? reducedChildren
: reducer(reducedChildren, self, nodePath, tree);
};
const reduceRecursiveOutwards = (reduction, self, childPath, reducer, tree, options, nodePath = List()) => {
if(options.onlyParents && isLeaf(self, childPath)) {
return reduction;
}
if(isLeaf(self, childPath)) {
return reducer(reduction, self, nodePath, tree);
}
const reducedSelf = options.onlyLeaves
? reduction
: reducer(reduction, self, nodePath, tree);
return self
.getIn(childPath)
.reduce((red, kid, key) => {
return reduceRecursiveOutwards(red, kid, childPath, reducer, tree, options, nodePath.push(key));
}, reducedSelf);
};
/**
* Once fully applied, this iterates through all nodes in the provided tree, passing them all through a `reducer` function.
* Nodes are processed branch by branch in the order that Immutable maps through the child iterables, inward from leaves to the root node.
* Sibling nodes are processed in the order that Immutable iterates through collections.
*
* Unlike Immutable.js, if the `initialReduction` is not provided or is null, then the initialReduction will be `null`.
*
* @param {Reducer} reducer The function to be called for every node in the tree, the results of which will be used to create the modified tree.
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node.
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`, and returns the modified tree `Iterable`.
*/
function deepReduce(reducer, initialReduction = null, childPath = null) {
return (tree) => reduceRecursive(initialReduction, tree, asList(childPath), reducer, tree, {});
}
/**
* Once fully applied, this iterates through the leaf nodes in the provided tree, passing them all through a `reducer` function. Leaf nodes are nodes that have no child nodes.
* Nodes are processed branch by branch, in the order that Immutable maps through the child iterables.
*
* Unlike Immutable.js, if the `initialReduction` is not provided or is null, then the initialReduction will be `null`.
*
* @param {Reducer} reducer The function to be called for every leaf node in the tree, the results of which will be used to create the modified tree.
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node.
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`, and returns the modified tree `Iterable`.
*/
function deepReduceLeaves(reducer, initialReduction = null, childPath = null) {
return (tree) => reduceRecursive(initialReduction, tree, asList(childPath), reducer, tree, {onlyLeaves: true});
}
/**
* Once fully applied, this iterates through the parent nodes in the provided tree, passing them all through a `reducer` function. Parent nodes are nodes that have child nodes.
* Nodes are processed branch by branch in the order that Immutable maps through the child iterables, inward from leaves to the root node.
*
* Unlike Immutable.js, if the `initialReduction` is not provided or is null, then the initialReduction will be `null`.
*
* @param {Reducer} reducer The function to be called for every parent node in the tree.
* the results of which will be used to create the modified iterable.
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node.
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`, and returns the modified tree `Iterable`.
*/
function deepReduceParents(reducer, initialReduction = null, childPath = null) {
return (tree) => reduceRecursive(initialReduction, tree, asList(childPath), reducer, tree, {onlyParents: true});
}
/**
* Once fully applied, this iterates through all nodes in the provided tree, passing them all through a `reducer` function.
* Nodes are processed branch by branch in the order that Immutable maps through the child iterables, outwards from the root node to the leaf nodes.
* Sibling nodes are processed in the order that Immutable iterates through collections.
*
* Unlike Immutable.js, if the `initialReduction` is not provided or is null, then the initialReduction will be `null`.
*
* @param {Reducer} reducer The function to be called for every node in the tree, the results of which will be used to create the modified tree.
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node.
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`, and returns the modified tree `Iterable`.
*/
function deepReduceOutwards(reducer, initialReduction = null, childPath = null) {
return (tree) => reduceRecursiveOutwards(initialReduction, tree, asList(childPath), reducer, tree, {});
}
/**
* Once fully applied, this iterates through the leaf nodes in the provided tree, passing them all through a `reducer` function.
* Nodes are processed branch by branch in the order that Immutable maps through the child iterables, outwards from the root node to the leaf nodes.
* Sibling nodes are processed in the order that Immutable iterates through collections.
*
* Because this moves from the root node outwards this function behaves quite differently to the other deep map functions.
*
* Unlike Immutable.js, if the `initialReduction` is not provided or is null, then the initialReduction will be `null`.
*
* @param {Reducer} reducer The function to be called for every leaf node in the tree, the results of which will be used to create the modified tree.
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node.
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`, and returns the modified tree `Iterable`.
*/
function deepReduceLeavesOutwards(reducer, initialReduction = null, childPath = null) {
return (tree) => reduceRecursiveOutwards(initialReduction, tree, asList(childPath), reducer, tree, {onlyLeaves: true});
}
/**
* Once fully applied, this iterates through the parent nodes in the provided tree, passing them all through a `reducer` function.
* Nodes are processed branch by branch in the order that Immutable maps through the child iterables, outwards from the root node to the leaf nodes.
* Sibling nodes are processed in the order that Immutable iterates through collections.
*
* Because this moves from the root node outwards this function behaves quite differently to the other deep map functions.
*
* Unlike Immutable.js, if the `initialReduction` is not provided or is null, then the initialReduction will be `null`.
*
* @param {Reducer} reducer The function to be called for every parent node in the tree, the results of which will be used to create the modified tree.
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node.
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`, and returns the modified tree `Iterable`.
*/
function deepReduceParentsOutwards(reducer, initialReduction = null, childPath = null) {
return (tree) => reduceRecursiveOutwards(initialReduction, tree, asList(childPath), reducer, tree, {onlyParents: true});
}
export {
deepReduce,
deepReduceLeaves,
deepReduceParents,
deepReduceOutwards,
deepReduceLeavesOutwards,
deepReduceParentsOutwards
}