import {
List
} from 'immutable';
import {
isLeaf,
asList
} from './utils';
/**
* @module map
*/
const mapRecursive = (self, childPath, mapper, tree, options, nodePath = List()) => {
const mapSelf = (self) => mapper(self, nodePath, tree);
if(isLeaf(self, childPath)) {
return options.onlyParents ? self : mapSelf(self);
}
const mappedChildren = self
.getIn(childPath)
.map((kid, key) => mapRecursive(kid, childPath, mapper, tree, options, nodePath.push(key)));
const selfWithMappedChildren = childPath.isEmpty()
? self.merge(mappedChildren)
: self.setIn(childPath, mappedChildren);
return options.onlyLeaves
? selfWithMappedChildren
: mapSelf(selfWithMappedChildren);
};
const mapRecursiveOutwards = (self, childPath, mapper, tree, options, nodePath = List()) => {
if(options.onlyParents && isLeaf(self, childPath)) {
return self;
}
var mappedSelf;
if(options.onlyLeaves) {
mappedSelf = self;
if(isLeaf(mappedSelf, childPath)) {
return mapper(self, nodePath, tree);
}
} else {
mappedSelf = mapper(self, nodePath, tree);
if(isLeaf(mappedSelf, childPath)) {
return mappedSelf;
}
}
return mappedSelf.updateIn(childPath, (children) => {
return children.map((kid, key) => mapRecursiveOutwards(kid, childPath, mapper, tree, options, nodePath.push(key)));
});
};
/**
* Once fully applied, this iterates through all nodes in the provided tree, passing them all through a `mapper` 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.
*
* `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 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 deepMap(mapper, childPath = null) {
return (tree) => mapRecursive(tree, asList(childPath), mapper, tree, {});
}
/**
* Once fully applied, this iterates 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 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 deepMapLeaves(mapper, childPath = null) {
return (tree) => mapRecursive(tree, asList(childPath), mapper, tree, {onlyLeaves: true});
}
/**
* Once fully applied, this iterates 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 branch by branch in the order that Immutable maps through the child iterables, inward from leaves to the root node.
*
* @param {Mapper} mapper 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 deepMapParents(mapper, childPath = null) {
return (tree) => mapRecursive(tree, asList(childPath), mapper, tree, {onlyParents: true});
}
/**
* Once fully applied, this iterates through all nodes in the provided tree, passing them all through a `mapper` 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.
*
* - If you modify a node's children, then those children will already be modified by the time they are called by the `mapper` function.
* - Because of the above, if a node removes or renames its own children iterable then those children will not be passed through the `mapper` function.
*
* @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 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 deepMapOutwards(mapper, childPath = null) {
return (tree) => mapRecursiveOutwards(tree, asList(childPath), mapper, tree, {});
}
/**
* Once fully applied, this iterates through the leaf nodes in the provided tree, passing them all through a `mapper` 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.
*
* @param {Mapper} mapper 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 deepMapLeavesOutwards(mapper, childPath = null) {
return (tree) => mapRecursiveOutwards(tree, asList(childPath), mapper, tree, {onlyLeaves: true});
}
/**
* Once fully applied, this iterates through the parent nodes in the provided tree, passing them all through a `mapper` 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.
*
* - If you modify a node's children, then those children will already be modified by the time they are called by the `mapper` function.
* - Because of the above, if a node removes or renames its own children iterable then those children will not be passed through the `mapper` function.
*
* @param {Mapper} mapper 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 deepMapParentsOutwards(mapper, childPath = null) {
return (tree) => mapRecursiveOutwards(tree, asList(childPath), mapper, tree, {onlyParents: true});
}
export {
deepMap,
deepMapLeaves,
deepMapParents,
deepMapOutwards,
deepMapLeavesOutwards,
deepMapParentsOutwards
}