import {
List
} from 'immutable';
import {
isLeaf,
asList
} from './utils';
/**
* @module map
*/
/**
* @callback inputFunction
* @param {Iterable} tree The tree Iterable to be processed by one of the deep functions.
* @return {Iterable} The modified iterable.
*/
/**
* @callback mapper
* @param {*} value The value of the current node
* @param {List} keys A List of all the keys to the current node
* @param {List|null} children A list of the current node's children
* @return {*} The replacement value.
*/
const mapRecursive = (self, childPath, mapper, options, keys = List()) => {
const mapSelf = (self, children) => mapper(self, keys, children);
if(isLeaf(self, childPath)) {
return options.onlyParents ? self : mapSelf(self, List());
}
const mappedChildren = self
.getIn(childPath)
.map((kid, key) => mapRecursive(kid, childPath, mapper, options, keys.push(key)));
const selfWithMappedChildren = childPath.isEmpty()
? self.merge(mappedChildren)
: self.setIn(childPath, mappedChildren);
return options.onlyLeaves
? selfWithMappedChildren
: mapSelf(selfWithMappedChildren, mappedChildren);
};
/**
* Maps through all nodes in the provided tree, passing them all through a mapper function.
* Nodes are processed inward from leaves to the root node, branch by branch, in the order that Immutable maps through the child iterables.
* Sibling nodes are processed in the order that Immutable.js iterates through collections.
*
* TODO: note about care with child collections, note about using as reduce
*
* @param {mapper} mapper The function to be called for every node in the tree,
* the results of which will be used to create the modified iterable.
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children.
* @return {inputFunction} A partially applied function which accepts a single tree interable, and returns the modified iterable.
*/
function deepMap(mapper, childPath = []) {
return (tree) => mapRecursive(tree, asList(childPath), mapper, {});
}
/**
* Maps through the leaf nodes in the provided tree, passing them all through a mapper 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.
*
* @param {mapper} mapper The function to be called for every node in the tree.
* the results of which will be used to create the modified iterable.
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children.
* @return {inputFunction} A partially applied function which accepts a single tree interable, and returns the modified iterable.
*/
function deepMapLeaves(mapper, childPath = []) {
return (tree) => mapRecursive(tree, asList(childPath), mapper, {onlyLeaves: true});
}
/**
* Maps through the parent nodes in the provided tree, passing them all through a mapper function. Parent nodes are nodes that have child nodes.
* Nodes are processed inward from leaves to the root node, branch by branch, in the order that Immutable maps through the child iterables.
*
* @param {mapper} mapper The function to be called for every node in the tree.
* the results of which will be used to create the modified iterable.
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children.
* @return {inputFunction} A partially applied function which accepts a single tree interable, and returns the modified iterable.
*/
function deepMapParents(mapper, childPath = []) {
return (tree) => mapRecursive(tree, asList(childPath), mapper, {onlyParents: true});
}
export {
deepMap,
deepMapLeaves,
deepMapParents
}