immutable-recursive
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -11,2 +11,5 @@ { | ||
}, | ||
"plugins": [ | ||
"plugins/markdown" | ||
], | ||
"docdash": { | ||
@@ -13,0 +16,0 @@ "sort": false |
@@ -15,81 +15,80 @@ 'use strict'; | ||
(0, _tape2.default)('asList', function (test) { | ||
test.plan(3); | ||
var nestedMaps = (0, _immutable.fromJS)({ | ||
a: { | ||
b: "B", | ||
c: "C", | ||
d: { | ||
e: "E" | ||
} | ||
}, | ||
f: "F" | ||
}); | ||
test.ok((0, _immutable.is)((0, _utils.asList)((0, _immutable.fromJS)(["a", "b"])), (0, _immutable.fromJS)(["a", "b"])), 'passes through Lists with no change'); | ||
var nestedLists = (0, _immutable.fromJS)([["A", "B", "C"], ["D", ["E", "F"]], "G"]); | ||
test.ok((0, _immutable.is)((0, _utils.asList)(["a", "b"]), (0, _immutable.fromJS)(["a", "b"])), 'converts arrays to Lists'); | ||
test.ok((0, _immutable.is)((0, _utils.asList)(null), (0, _immutable.List)()), 'converts null to an empty List'); | ||
var nodesWithChildCollections = (0, _immutable.fromJS)({ | ||
name: "root", | ||
children: [{ | ||
name: "child 1", | ||
children: [{ name: "grandchild 1" }, { name: "grandchild 2" }] | ||
}, { | ||
name: "child 2", | ||
children: [] | ||
}, { name: "child 3" }] | ||
}); | ||
(0, _tape2.default)('isLeaf', function (test) { | ||
(0, _tape2.default)('asList', function (test) { | ||
test.plan(3); | ||
var nestedMaps = (0, _immutable.fromJS)({ | ||
a: { | ||
b: "B", | ||
c: "C", | ||
d: { | ||
e: "E" | ||
} | ||
}, | ||
f: "F" | ||
}); | ||
test.ok((0, _immutable.is)((0, _utils.asList)((0, _immutable.fromJS)(["a", "b"])), (0, _immutable.fromJS)(["a", "b"])), 'passes through Lists with no change'); | ||
var nestedLists = (0, _immutable.fromJS)([["A", "B", "C"], ["D", ["E", "F"]], "G"]); | ||
test.ok((0, _immutable.is)((0, _utils.asList)(["a", "b"]), (0, _immutable.fromJS)(["a", "b"])), 'converts arrays to Lists'); | ||
var nodesWithChildCollections = (0, _immutable.fromJS)({ | ||
name: "root", | ||
children: [{ | ||
name: "child 1", | ||
children: [{ name: "grandchild 1" }, { name: "grandchild 2" }] | ||
}, { | ||
name: "child 2", | ||
children: [] | ||
}, { name: "child 3" }] | ||
}); | ||
test.ok((0, _immutable.is)((0, _utils.asList)(null), (0, _immutable.List)()), 'converts null to an empty List'); | ||
}); | ||
test.plan(7); | ||
(0, _tape2.default)('isLeaf', function (test) { | ||
test.plan(7); | ||
test.notOk((0, _utils.isLeaf)(nestedMaps.get('a')), 'is false if node is a map'); | ||
test.ok((0, _utils.isLeaf)(nestedMaps.getIn(['a', 'b'])), 'is true if node is not a map'); | ||
test.notOk((0, _utils.isLeaf)(nestedMaps.get('a')), 'is false if node is a map'); | ||
test.ok((0, _utils.isLeaf)(nestedMaps.getIn(['a', 'b'])), 'is true if node is not a map'); | ||
test.notOk((0, _utils.isLeaf)(nestedLists.getIn([1, 1])), 'is false if node is a list'); | ||
test.ok((0, _utils.isLeaf)(nestedLists.get(2)), 'is true if node is not a list'); | ||
test.notOk((0, _utils.isLeaf)(nestedLists.getIn([1, 1])), 'is false if node is a list'); | ||
test.ok((0, _utils.isLeaf)(nestedLists.get(2)), 'is true if node is not a list'); | ||
var nodeChild1 = (0, _get.deepGet)([0], ['children'])(nodesWithChildCollections); | ||
var nodeChild2 = (0, _get.deepGet)([1], ['children'])(nodesWithChildCollections); | ||
var nodeChild3 = (0, _get.deepGet)([2], ['children'])(nodesWithChildCollections); | ||
var nodeChild1 = (0, _get.deepGet)([0], ['children'])(nodesWithChildCollections); | ||
var nodeChild2 = (0, _get.deepGet)([1], ['children'])(nodesWithChildCollections); | ||
var nodeChild3 = (0, _get.deepGet)([2], ['children'])(nodesWithChildCollections); | ||
test.notOk((0, _utils.isLeaf)(nodeChild1, ['children']), 'is false if node has a child iterable'); | ||
test.ok((0, _utils.isLeaf)(nodeChild3, ['children']), 'is true if node has no child iterable'); | ||
test.ok((0, _utils.isLeaf)(nodeChild2, ['children']), 'is true if node has empty child iterable'); | ||
test.notOk((0, _utils.isLeaf)(nodeChild1, ['children']), 'is false if node has a child iterable'); | ||
test.ok((0, _utils.isLeaf)(nodeChild3, ['children']), 'is true if node has no child iterable'); | ||
test.ok((0, _utils.isLeaf)(nodeChild2, ['children']), 'is true if node has empty child iterable'); | ||
}); | ||
(0, _tape2.default)('keysToPath', function (test) { | ||
test.plan(5); | ||
(0, _tape2.default)('nodePathToKeys', function (test) { | ||
test.plan(5); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPath)([0, 1]), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is not set'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeys)([0, 1]), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is not set'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPath)([0, 1], []), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is empty array'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeys)([0, 1], []), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is empty array'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPath)([0], ['children']), (0, _immutable.fromJS)(['children', 0])), 'path is simply childPath with key appended if only one key'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeys)([0], ['children']), (0, _immutable.fromJS)(['children', 0])), 'path is simply childPath with key appended if only one key'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPath)([0, 2, 4], ['children']), (0, _immutable.fromJS)(['children', 0, 'children', 2, 'children', 4])), 'path adds childPath before every key in keys'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeys)([0, 2, 4], ['children']), (0, _immutable.fromJS)(['children', 0, 'children', 2, 'children', 4])), 'path adds childPath before every key in keys'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPath)([0, 1], ['data', 'children']), (0, _immutable.fromJS)(['data', 'children', 0, 'data', 'children', 1])), 'can cope with childPaths longer than one'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeys)([0, 1], ['data', 'children']), (0, _immutable.fromJS)(['data', 'children', 0, 'data', 'children', 1])), 'can cope with childPaths longer than one'); | ||
}); | ||
(0, _tape2.default)('keysToPathChildren', function (test) { | ||
test.plan(5); | ||
(0, _tape2.default)('nodePathToKeysChildren', function (test) { | ||
test.plan(5); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPathChildren)([0, 1]), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is not set'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeysChildren)([0, 1]), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is not set'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPathChildren)([0, 1], []), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is empty array'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeysChildren)([0, 1], []), (0, _immutable.fromJS)([0, 1])), 'path is same as keys if childPath is empty array'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPathChildren)([], ['children']), (0, _immutable.fromJS)(['children'])), 'path is simply childPath if no keys given'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeysChildren)([], ['children']), (0, _immutable.fromJS)(['children'])), 'path is simply childPath if no keys given'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPathChildren)([0, 2, 4], ['children']), (0, _immutable.fromJS)(['children', 0, 'children', 2, 'children', 4, 'children'])), 'path adds childPath before every key in keys + 1'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeysChildren)([0, 2, 4], ['children']), (0, _immutable.fromJS)(['children', 0, 'children', 2, 'children', 4, 'children'])), 'path adds childPath before every key in keys + 1'); | ||
test.ok((0, _immutable.is)((0, _utils.keysToPathChildren)([0, 1], ['data', 'children']), (0, _immutable.fromJS)(['data', 'children', 0, 'data', 'children', 1, 'data', 'children'])), 'can cope with childPaths longer than one'); | ||
test.ok((0, _immutable.is)((0, _utils.nodePathToKeysChildren)([0, 1], ['data', 'children']), (0, _immutable.fromJS)(['data', 'children', 0, 'data', 'children', 1, 'data', 'children'])), 'can cope with childPaths longer than one'); | ||
}); |
@@ -15,15 +15,16 @@ 'use strict'; | ||
/** | ||
* Returns a specific node from the tree. When no childPath is provided then this is functionally equivalent to Immutable's getIn function. | ||
* Returns a specific node from the tree. | ||
* When no `childPath` is provided then this is functionally equivalent to Immutable's `getIn()` function. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to keys | ||
* @return {inputFunction} The node at the specified path of keys, or if no node exists then notSetValue will be returned | ||
* @param {NodePath} nodePath A `NodePath` used to identify the node to return. | ||
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to `nodePath`. | ||
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`. `InputFunction` will return the node at the specified `nodePath`, or if no node exists then `notSetValue` will be returned. | ||
*/ | ||
function deepGet(keys, childPath) { | ||
function deepGet(nodePath, childPath) { | ||
var notSetValue = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; | ||
return function (tree) { | ||
return tree.getIn((0, _utils.keysToPath)(keys, childPath), notSetValue); | ||
return tree.getIn((0, _utils.nodePathToKeys)(nodePath, childPath), notSetValue); | ||
}; | ||
@@ -34,14 +35,15 @@ } | ||
* Returns the children of a specific node from the tree. | ||
* When no `childPat`h is provided then this is functionally equivalent to Immutable's `getIn()` function or `deepMap()`. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify nested nodes. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to keys, or if the node doesn't have any children | ||
* @return {inputFunction} The children of the node at the specified path of keys, or if no node or children exist then notSetValue will be returned | ||
* @param {NodePath} nodePath A `NodePath` used to identify the node to return. | ||
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to `nodePath`, or if the node doesn't have any children. | ||
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`. `InputFunction` will return the children of the node at the specified `nodePath`, or if no node or children exist then `notSetValue` will be returned | ||
*/ | ||
function deepGetChildren(keys, childPath) { | ||
function deepGetChildren(nodePath, childPath) { | ||
var notSetValue = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; | ||
return function (tree) { | ||
return tree.getIn((0, _utils.keysToPathChildren)(keys, childPath), notSetValue); | ||
return tree.getIn((0, _utils.nodePathToKeysChildren)(nodePath, childPath), notSetValue); | ||
}; | ||
@@ -48,0 +50,0 @@ } |
@@ -16,21 +16,7 @@ 'use strict'; | ||
/** | ||
* @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. | ||
*/ | ||
var mapRecursive = function mapRecursive(self, childPath, mapper, options) { | ||
var keys = arguments.length <= 4 || arguments[4] === undefined ? (0, _immutable.List)() : arguments[4]; | ||
var nodePath = arguments.length <= 4 || arguments[4] === undefined ? (0, _immutable.List)() : arguments[4]; | ||
var mapSelf = function mapSelf(self, children) { | ||
return mapper(self, keys, children); | ||
return mapper(self, nodePath, children); | ||
}; | ||
@@ -43,3 +29,3 @@ | ||
var mappedChildren = self.getIn(childPath).map(function (kid, key) { | ||
return mapRecursive(kid, childPath, mapper, options, keys.push(key)); | ||
return mapRecursive(kid, childPath, mapper, options, nodePath.push(key)); | ||
}); | ||
@@ -53,16 +39,15 @@ | ||
/** | ||
* 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. | ||
* Maps 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 | ||
* `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. | ||
* @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) { | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | ||
@@ -78,10 +63,9 @@ return function (tree) { | ||
* | ||
* @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. | ||
* @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 deepMapLeaves(mapper) { | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | ||
@@ -95,12 +79,12 @@ return function (tree) { | ||
* 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. | ||
* 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 node in the tree. | ||
* @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. | ||
* @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) { | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | ||
@@ -107,0 +91,0 @@ return function (tree) { |
@@ -15,21 +15,21 @@ 'use strict'; | ||
/** | ||
* Once fully applied, this returns a new [tree] containing the new node [value] at the location determined by [keys]. | ||
* If an equivalent node already exists in this [tree] it will be replaced. | ||
* If any [keys] do not exist, a new Immutable Map will be created at the missing key. | ||
* Once fully applied, this returns a new tree containing the new node `value` at the location determined by `nodePath`. | ||
* If an equivalent node already exists in this tree it will be replaced. | ||
* If any keys on `nodePath` do not exist, a new Immutable `Map` will be created at the missing key. | ||
* | ||
* Be aware that by setting values on deep nodes that don't yet exist, | ||
* this may cause Maps to be created for child Iterables where other Iterables may be used for children elsewhere in the tree. | ||
* this may cause `Maps` to be created for child `Iterables` where other types of `Iterables` may be used for children elsewhere in the tree. | ||
* This will be addressed in a future release. | ||
* | ||
* When no childPath is provided then this is functionally equivalent to Immutable's setIn function. | ||
* When no `childPath` is provided then this is functionally equivalent to Immutable's `setIn()` method. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node to set. | ||
* @param {NodePath} nodePath A NodePath used to uniquely identify the node to set. | ||
* @param value The value to set the node to. | ||
* @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. | ||
* @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 deepSet(keys, value, childPath) { | ||
function deepSet(nodePath, value, childPath) { | ||
return function (tree) { | ||
return tree.setIn((0, _utils.keysToPath)(keys, childPath), value); | ||
return tree.setIn((0, _utils.nodePathToKeys)(nodePath, childPath), value); | ||
}; | ||
@@ -39,15 +39,15 @@ } | ||
/** | ||
* Once fully applied, this returns a new tree where the children of the node at [keys] are set to [value]. | ||
* If no node exists at [keys] then an Immutable Map will be created at that location. | ||
* When no childPath is provided then this is functionally equivalent to Immutable's setIn function. | ||
* Once fully applied, this returns a new tree where the children `Iterable` of the node at `nodePath` are set to `value`. | ||
* If no node exists at `nodePath` then an Immutable `Map` will be created at that location. | ||
* When no `childPath` is provided then this is functionally equivalent to both Immutable's `setIn()` method and `deepSet()`. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node to set. | ||
* @param {NodePath} nodePath A `NodePath` used to uniquely identify a node whose children will be set. | ||
* @param value The value to set the node to. | ||
* @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. | ||
* @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 deepSetChildren(keys, value, childPath) { | ||
function deepSetChildren(nodePath, value, childPath) { | ||
return function (tree) { | ||
return tree.setIn((0, _utils.keysToPathChildren)(keys, childPath), value); | ||
return tree.setIn((0, _utils.nodePathToKeysChildren)(nodePath, childPath), value); | ||
}; | ||
@@ -54,0 +54,0 @@ } |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.keysToPathChildren = exports.keysToPath = exports.isLeaf = exports.asList = undefined; | ||
exports.isLeaf = exports.nodePathToKeysChildren = exports.nodePathToKeys = exports.asList = undefined; | ||
@@ -18,2 +18,7 @@ var _immutable = require('immutable'); | ||
* | ||
* @example | ||
* asList(null); // returns List() | ||
* asList([0,1,2]); // returns List().of(0,1,2) | ||
* asList(List().of(0,1,2)); // returns List().of(0,1,2) | ||
* | ||
* @param {Array|List|null} input The input to be convert to a List. List items will pass through unchanged, all others will be passed into a List constructor. | ||
@@ -28,53 +33,86 @@ * @return {List} The equivalent List. | ||
/** | ||
* Accepts a node and returns a boolean indicating if the node is a leaf node (i.e. it has no children). | ||
* Turns a node's nodePath and its childPath into a full key path. | ||
* | ||
* @param {*} node The node to check. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @return {boolean} A boolean indicating if the node is a leaf node. | ||
* @example | ||
* nodePathToKeys(null, ['children']); // returns [] | ||
* nodePathToKeys(['bob'], ['children']); // returns ['children', 'bob'] | ||
* nodePathToKeys([0,1], ['children']); // returns ['children', 0, 'children', 1] | ||
* | ||
* @param {NodePath} nodePath A NodePath used to uniquely identify a node. | ||
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find each node's children from within each node. | ||
* @return {List} | ||
*/ | ||
function isLeaf(node) { | ||
function nodePathToKeys(nodePath) { | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | ||
if (!_immutable.Iterable.isIterable(node)) { | ||
return true; | ||
} | ||
var children = node.getIn(childPath || []); | ||
return !children || children.isEmpty(); | ||
childPath = asList(childPath); | ||
return asList(nodePath).reduce(function (fullPath, key) { | ||
return fullPath.concat(childPath).push(key); | ||
}, (0, _immutable.List)()); | ||
} | ||
/** | ||
* Turns a node's keys and its childPath into a full path. | ||
* Turns a node's nodePath and its childPath into a full key path to the node's children. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @example | ||
* nodePathToKeysChildren(null, ['children']); // returns ['children'] | ||
* nodePathToKeysChildren(['bob'], ['children']); // returns ['children', 'bob', children'] | ||
* nodePathToKeysChildren([0,1], ['children']); // returns ['children', 0, 'children', 1, 'children'] | ||
* | ||
* @param {NodePath} nodePath A NodePath used to uniquely identify a node. | ||
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find each node's children from within each node. | ||
* @return {List} | ||
*/ | ||
function keysToPath(keys) { | ||
function nodePathToKeysChildren(nodePath) { | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | ||
childPath = asList(childPath); | ||
return keys.reduce(function (fullPath, key) { | ||
return fullPath.concat(childPath).push(key); | ||
}, (0, _immutable.List)()); | ||
return childPath.isEmpty() ? nodePathToKeys(nodePath) : nodePathToKeys(nodePath, childPath).concat(childPath); | ||
} | ||
/** | ||
* Turns a node's keys and its childPath into a full path to the node's children. | ||
* Accepts a node and returns a boolean indicating if the node is a leaf node (i.e. it has no children). | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @return {List} | ||
* @example | ||
* const tree = fromJS({ | ||
* name: "root", | ||
* children: [ | ||
* { | ||
* name: "child 1", | ||
* children: [ | ||
* {name: "grandchild 1"}, | ||
* {name: "grandchild 2"} | ||
* ] | ||
* }, | ||
* { | ||
* name: "child 2", | ||
* children: [] | ||
* } | ||
* ] | ||
* }); | ||
* | ||
* isLeaf(tree.getIn(['children', 0]), ['childPath']); // returns false because child 1 has children | ||
* isLeaf(tree.getIn(['children', 1]), ['childPath']); // returns true because child 2 has no children | ||
* // note that normally deepGet is recommended instead of getIn for getting nodes from a tree | ||
* | ||
* @param {*} node The node to check. | ||
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find each node's children from within each node. | ||
* @return {boolean} A boolean indicating if the node is a leaf node. | ||
*/ | ||
function keysToPathChildren(keys) { | ||
function isLeaf(node) { | ||
var childPath = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | ||
return childPath == null ? asList(keys) : keysToPath(keys, childPath).concat(childPath); | ||
if (!_immutable.Iterable.isIterable(node)) { | ||
return true; | ||
} | ||
var children = node.getIn(childPath || []); | ||
return !children || children.isEmpty(); | ||
} | ||
exports.asList = asList; | ||
exports.isLeaf = isLeaf; | ||
exports.keysToPath = keysToPath; | ||
exports.keysToPathChildren = keysToPathChildren; | ||
exports.nodePathToKeys = nodePathToKeys; | ||
exports.nodePathToKeysChildren = nodePathToKeysChildren; | ||
exports.isLeaf = isLeaf; |
{ | ||
"name": "immutable-recursive", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Extra functions for use with Immutable.js to better handle tree structures / nested data and recursive iteration", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -10,3 +10,3 @@ # immutable-recursive | ||
[Preliminary documentation](https://dxinteractive.github.io/immutable-recursive/) | ||
[Github](https://github.com/dxinteractive/immutable-recursive/) | [Preliminary documentation](https://dxinteractive.github.io/immutable-recursive/) | ||
@@ -17,4 +17,4 @@ ## Development tasks | ||
| -------- | ---- | ----- | ----- | -------- | -------- | | ||
| deepGet | Done | Done | | | | | ||
| deepGetChildren | Done | Done | | | | | ||
| deepGet | Done | Done | Done | Done | | | ||
| deepGetChildren | Done | Done | Done | Done | | | ||
| deepSet | Done | Done | | | | | ||
@@ -35,3 +35,2 @@ | deepSetChildren | Done | Done | | | | | ||
| deepFilterCollapse | In progress | | | | | | ||
| deepFilterCollapse | In progress | | | | | | ||
| deepSortBy | Done | In progress | | | | | ||
@@ -42,21 +41,25 @@ | deconstruct | Done | In progress | | | | | ||
| flatPrune | | | | | | | ||
| asList | Done | Done | Done | Done | | | ||
| isLeaf | Done | Done | Done | Done | | | ||
| keysToPath | Done | Done | Done | Done | | | ||
| keysToPathChildren | Done | Done | Done | Done | | | ||
| asList | Done | Done | Done | Done | Done | | ||
| nodePathToKeys | Done | Done | Done | Done | Done | | ||
| nodePathToKeysChildren | Done | Done | Done | Done | Done | | ||
| isLeaf | Done | Done | Done | Done | Done | | ||
| isSiblingOf | Done | | | | | | ||
| isParentOf | Done | | | | | | ||
| isAncestorOf | Done | | | | | | ||
| commonAncestorOf | | | | | | | ||
| inputFunction (typedef) | Done | N/A | N/A | Done | | | ||
| keys (typedef) | Done | N/A | N/A | In progress | | | ||
| childPath (typedef) | Done | N/A | N/A | In progress | | | ||
| mapper (typedef) | Done | N/A | N/A | In progress | | | ||
| updater (typedef) | Done | N/A | N/A | In progress | | | ||
| reducer (typedef) | Done | N/A | N/A | In progress | | | ||
| filter (typedef) | Done | N/A | N/A | In progress | | | ||
| commonAncestor | | | | | | | ||
| Typedef | Plan | Document | Examples | ||
| ------- | ---- | -------- | -------- | | ||
| InputFunction | Done | Done | Done | | ||
| NodePath | Done | Done | Done | | ||
| ChildPath | Done | Done | Done | | ||
| mapper | Done | Done | | | ||
| updater | Done | In progress | | | ||
| reducer | Done | In progress | | | ||
| filter | Done | In progress | | | ||
- Reconsider paramaters passed to mappers / updaters etc. and which children, parents or ancestors should be accessible | ||
- Try adding @externals for JS and Immutable, try grouping under different modules | ||
- Allow functions to be passsed into ChildPath and modify all affected library functions so they can use it | ||
- Add a 'thinking in trees and nodes' style intro | ||
- Add documentation about partially applied function benefits | ||
- Add documentation about deep data manipulation pitfalls, avoiding killing children | ||
@@ -66,2 +69,2 @@ - Add build process with Travis | ||
- Add shields for tests, version numbers, coverage | ||
- Change to minami, fix table styles on index page | ||
- Fix table styles on index page, remove horrible purple colour |
@@ -14,4 +14,4 @@ import tape from 'tape'; | ||
isLeaf, | ||
keysToPath, | ||
keysToPathChildren | ||
nodePathToKeys, | ||
nodePathToKeysChildren | ||
} from '../utils'; | ||
@@ -23,2 +23,37 @@ | ||
const nestedMaps = fromJS({ | ||
a: { | ||
b: "B", | ||
c: "C", | ||
d: { | ||
e: "E" | ||
} | ||
}, | ||
f: "F" | ||
}); | ||
const nestedLists = fromJS([ | ||
["A","B","C"], | ||
["D",["E","F"]], | ||
"G" | ||
]); | ||
const nodesWithChildCollections = fromJS({ | ||
name: "root", | ||
children: [ | ||
{ | ||
name: "child 1", | ||
children: [ | ||
{name: "grandchild 1"}, | ||
{name: "grandchild 2"} | ||
] | ||
}, | ||
{ | ||
name: "child 2", | ||
children: [] | ||
}, | ||
{name: "child 3"} | ||
] | ||
}); | ||
tape('asList', test => { | ||
@@ -44,38 +79,2 @@ test.plan(3); | ||
tape('isLeaf', test => { | ||
const nestedMaps = fromJS({ | ||
a: { | ||
b: "B", | ||
c: "C", | ||
d: { | ||
e: "E" | ||
} | ||
}, | ||
f: "F" | ||
}); | ||
const nestedLists = fromJS([ | ||
["A","B","C"], | ||
["D",["E","F"]], | ||
"G" | ||
]); | ||
const nodesWithChildCollections = fromJS({ | ||
name: "root", | ||
children: [ | ||
{ | ||
name: "child 1", | ||
children: [ | ||
{name: "grandchild 1"}, | ||
{name: "grandchild 2"} | ||
] | ||
}, | ||
{ | ||
name: "child 2", | ||
children: [] | ||
}, | ||
{name: "child 3"} | ||
] | ||
}); | ||
test.plan(7); | ||
@@ -98,7 +97,7 @@ | ||
tape('keysToPath', test => { | ||
tape('nodePathToKeys', test => { | ||
test.plan(5); | ||
test.ok(is( | ||
keysToPath([0,1]), | ||
nodePathToKeys([0,1]), | ||
fromJS([0,1]) | ||
@@ -108,3 +107,3 @@ ), 'path is same as keys if childPath is not set'); | ||
test.ok(is( | ||
keysToPath([0,1],[]), | ||
nodePathToKeys([0,1],[]), | ||
fromJS([0,1]) | ||
@@ -114,3 +113,3 @@ ), 'path is same as keys if childPath is empty array'); | ||
test.ok(is( | ||
keysToPath([0],['children']), | ||
nodePathToKeys([0],['children']), | ||
fromJS(['children',0]) | ||
@@ -120,3 +119,3 @@ ), 'path is simply childPath with key appended if only one key'); | ||
test.ok(is( | ||
keysToPath([0,2,4],['children']), | ||
nodePathToKeys([0,2,4],['children']), | ||
fromJS(['children',0,'children',2,'children',4]) | ||
@@ -126,3 +125,3 @@ ), 'path adds childPath before every key in keys'); | ||
test.ok(is( | ||
keysToPath([0,1],['data','children']), | ||
nodePathToKeys([0,1],['data','children']), | ||
fromJS(['data','children',0,'data','children',1]) | ||
@@ -132,7 +131,7 @@ ), 'can cope with childPaths longer than one'); | ||
tape('keysToPathChildren', test => { | ||
tape('nodePathToKeysChildren', test => { | ||
test.plan(5); | ||
test.ok(is( | ||
keysToPathChildren([0,1]), | ||
nodePathToKeysChildren([0,1]), | ||
fromJS([0,1]) | ||
@@ -142,3 +141,3 @@ ), 'path is same as keys if childPath is not set'); | ||
test.ok(is( | ||
keysToPathChildren([0,1],[]), | ||
nodePathToKeysChildren([0,1],[]), | ||
fromJS([0,1]) | ||
@@ -148,3 +147,3 @@ ), 'path is same as keys if childPath is empty array'); | ||
test.ok(is( | ||
keysToPathChildren([],['children']), | ||
nodePathToKeysChildren([],['children']), | ||
fromJS(['children']) | ||
@@ -154,3 +153,3 @@ ), 'path is simply childPath if no keys given'); | ||
test.ok(is( | ||
keysToPathChildren([0,2,4],['children']), | ||
nodePathToKeysChildren([0,2,4],['children']), | ||
fromJS(['children',0,'children',2,'children',4,'children']) | ||
@@ -160,5 +159,5 @@ ), 'path adds childPath before every key in keys + 1'); | ||
test.ok(is( | ||
keysToPathChildren([0,1],['data','children']), | ||
nodePathToKeysChildren([0,1],['data','children']), | ||
fromJS(['data','children',0,'data','children',1,'data','children']) | ||
), 'can cope with childPaths longer than one'); | ||
}); |
import { | ||
keysToPath, | ||
keysToPathChildren | ||
nodePathToKeys, | ||
nodePathToKeysChildren | ||
} from './utils'; | ||
@@ -11,12 +11,13 @@ | ||
/** | ||
* Returns a specific node from the tree. When no childPath is provided then this is functionally equivalent to Immutable's getIn function. | ||
* Returns a specific node from the tree. | ||
* When no `childPath` is provided then this is functionally equivalent to Immutable's `getIn()` function. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to keys | ||
* @return {inputFunction} The node at the specified path of keys, or if no node exists then notSetValue will be returned | ||
* @param {NodePath} nodePath A `NodePath` used to identify the node to return. | ||
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to `nodePath`. | ||
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`. `InputFunction` will return the node at the specified `nodePath`, or if no node exists then `notSetValue` will be returned. | ||
*/ | ||
function deepGet(keys, childPath, notSetValue = null) { | ||
return (tree) => tree.getIn(keysToPath(keys, childPath), notSetValue); | ||
function deepGet(nodePath, childPath, notSetValue = null) { | ||
return (tree) => tree.getIn(nodePathToKeys(nodePath, childPath), notSetValue); | ||
} | ||
@@ -26,11 +27,12 @@ | ||
* Returns the children of a specific node from the tree. | ||
* When no `childPat`h is provided then this is functionally equivalent to Immutable's `getIn()` function or `deepMap()`. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify nested nodes. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to keys, or if the node doesn't have any children | ||
* @return {inputFunction} The children of the node at the specified path of keys, or if no node or children exist then notSetValue will be returned | ||
* @param {NodePath} nodePath A `NodePath` used to identify the node to return. | ||
* @param {ChildPath} [childPath=null] An `Array` or `List` of keys indicating where to find each node's children from within each node. | ||
* @param [notSetValue=null] A value to return when there is no node corresponding to `nodePath`, or if the node doesn't have any children. | ||
* @return {InputFunction} A partially applied function which accepts a single tree `Iterable`. `InputFunction` will return the children of the node at the specified `nodePath`, or if no node or children exist then `notSetValue` will be returned | ||
*/ | ||
function deepGetChildren(keys, childPath, notSetValue = null) { | ||
return (tree) => tree.getIn(keysToPathChildren(keys, childPath), notSetValue); | ||
function deepGetChildren(nodePath, childPath, notSetValue = null) { | ||
return (tree) => tree.getIn(nodePathToKeysChildren(nodePath, childPath), notSetValue); | ||
} | ||
@@ -37,0 +39,0 @@ |
154
src/index.js
@@ -0,1 +1,151 @@ | ||
/** | ||
* When passed as a parameter, `NodePath` can be a `List` or an `Array` of keys used to uniquely identify a node. | ||
* Almost always used in conjunction with a `ChildPath`. | ||
* Also accepts null in place of an empty `Array` or `List`. | ||
* | ||
* Whenever `NodePath` is returned from a function in the immutable-recursive library, | ||
* it will always be a `List`. | ||
* | ||
* @example | ||
* const tree = fromJS({ | ||
* name: "root", | ||
* children: [ | ||
* { | ||
* name: "child 1", | ||
* children: [ | ||
* {name: "grandchild 1"}, | ||
* {name: "grandchild 2"} | ||
* ] | ||
* } | ||
* {name: "child 2"} | ||
* ] | ||
* }); | ||
* // The NodePath for "root" is [] (or null) | ||
* // The NodePath for "child 1" is [0] | ||
* // The NodePath for "grandchild 2" is [0,1] | ||
* | ||
* const nestedMaps = fromJS({ | ||
* a: { | ||
* b: "B", | ||
* c: "C", | ||
* d: { | ||
* e: "E" | ||
* } | ||
* }, | ||
* f: "F" | ||
* }); | ||
* // The NodePath for "A" is ['a'] | ||
* // The NodePath for "B" is ['a','b'] | ||
* // The NodePath for "E" is ['a','d','e'] | ||
* | ||
* @typedef {Array|List|null} NodePath | ||
*/ | ||
/** | ||
* An `Array` or `List` of keys indicating where to find each node's children from within each node. | ||
* This works in the same way as the `keyPath` parameter in Immutable's `getIn()` method. | ||
* It also accepts null in place of an empty `Array` or `List`. | ||
* | ||
* @example | ||
* // this tree would have a ChildPath of ['children'], as each node's children can be found in the 'children' iterable | ||
* const tree = fromJS({ | ||
* name: "root", | ||
* children: [ | ||
* { | ||
* name: "child 1", | ||
* children: [ | ||
* {name: "grandchild 1"}, | ||
* {name: "grandchild 2"} | ||
* ] | ||
* }, | ||
* { | ||
* name: "child 2", | ||
* children: [] | ||
* }, | ||
* {name: "child 3"} | ||
* ] | ||
* }); | ||
* | ||
* // nested maps and lists like these don't require a ChildPath, as each Iterable contains its own children | ||
* const nestedMaps = fromJS({ | ||
* a: { | ||
* b: "B", | ||
* c: "C", | ||
* d: { | ||
* e: "E" | ||
* } | ||
* }, | ||
* f: "F" | ||
* }); | ||
* | ||
* const nestedLists = fromJS([ | ||
* ["A","B","C"], | ||
* ["D",["E","F"]], | ||
* "G" | ||
* ); | ||
* | ||
* @typedef {Array|List|null} ChildPath | ||
*/ | ||
/** | ||
* InputFunction is a partially applied function returned from all 'deep' functions in immutable-recursive. | ||
* It accepts an iterable, the tree of data you wish to call your deep function on, | ||
* and returns the result of the deep function you're calling. | ||
* | ||
* For example, one way to use `deepGet` is to call it, passing in any parameters required, then call the returned function, passing in your data tree. | ||
* | ||
* ```js | ||
* const nestedMaps = fromJS({ | ||
* a: { | ||
* b: "B", | ||
* c: "C", | ||
* d: { | ||
* e: "E" | ||
* } | ||
* }, | ||
* f: "F" | ||
* }); | ||
* | ||
* return deepGet(['a','b'])(nestedMaps); // returns "B" | ||
* ``` | ||
* | ||
* This design of using a partially applied function allows immutable-recursive's functions to be chained easily with Immutable, | ||
* by using them inside of an `update()` method. This is the preferred usage pattern as it retains chainability. | ||
* | ||
* ```js | ||
* return fromJS(someData) | ||
* .get('tree') | ||
* .update(deepGet(['a','b'])) // using deepGet in a chain | ||
* .set('done', true) | ||
* .toJS(); | ||
* ``` | ||
* | ||
* You can also define a function to perform a specific operation, and use it multiple times by passing in different tree data. | ||
* | ||
* ```js | ||
* // make every node's name uppercase | ||
* const uppercaseNames = deepMap((node) => { | ||
* return node.update('name', (name) => name.toUpperCase()); | ||
* }, ['children']); | ||
* | ||
* const a = fromJS(aDataTree).update(uppercaseNames); | ||
* const b = fromJS(aDifferentDataTree).update(uppercaseNames); | ||
* ``` | ||
* | ||
* @callback InputFunction | ||
* @param {Iterable} tree The tree Iterable to be processed by one of the deep functions. | ||
* @return {Iterable} The modified iterable. | ||
*/ | ||
/** | ||
* A function required as an argument for the `deepMap` functions. | ||
* | ||
* @callback Mapper | ||
* @param {*} value The value of the current node. | ||
* @param {List} nodePath The current node's `NodePath`, a `List` of keys used to uniquely identify the current node. | ||
* @param {List} children A `List` of the current node's children. | ||
* @return {*} The replacement value. | ||
*/ | ||
export { | ||
@@ -19,5 +169,5 @@ deepGet, | ||
asList, | ||
isLeaf, | ||
keysToPath, | ||
keysToPathChildren | ||
keysToPathChildren, | ||
isLeaf | ||
} from './utils'; |
@@ -14,18 +14,4 @@ import { | ||
/** | ||
* @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); | ||
const mapRecursive = (self, childPath, mapper, options, nodePath = List()) => { | ||
const mapSelf = (self, children) => mapper(self, nodePath, children); | ||
@@ -38,3 +24,3 @@ if(isLeaf(self, childPath)) { | ||
.getIn(childPath) | ||
.map((kid, key) => mapRecursive(kid, childPath, mapper, options, keys.push(key))); | ||
.map((kid, key) => mapRecursive(kid, childPath, mapper, options, nodePath.push(key))); | ||
@@ -52,15 +38,14 @@ const selfWithMappedChildren = childPath.isEmpty() | ||
/** | ||
* 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. | ||
* Maps 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 | ||
* `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. | ||
* @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 = []) { | ||
function deepMap(mapper, childPath = null) { | ||
return (tree) => mapRecursive(tree, asList(childPath), mapper, {}); | ||
@@ -73,9 +58,8 @@ } | ||
* | ||
* @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. | ||
* @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 deepMapLeaves(mapper, childPath = []) { | ||
function deepMapLeaves(mapper, childPath = null) { | ||
return (tree) => mapRecursive(tree, asList(childPath), mapper, {onlyLeaves: true}); | ||
@@ -86,11 +70,11 @@ } | ||
* 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. | ||
* 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 node in the tree. | ||
* @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. | ||
* @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 = []) { | ||
function deepMapParents(mapper, childPath = null) { | ||
return (tree) => mapRecursive(tree, asList(childPath), mapper, {onlyParents: true}); | ||
@@ -97,0 +81,0 @@ } |
import { | ||
keysToPath, | ||
keysToPathChildren | ||
nodePathToKeys, | ||
nodePathToKeysChildren | ||
} from './utils'; | ||
@@ -11,35 +11,35 @@ | ||
/** | ||
* Once fully applied, this returns a new [tree] containing the new node [value] at the location determined by [keys]. | ||
* If an equivalent node already exists in this [tree] it will be replaced. | ||
* If any [keys] do not exist, a new Immutable Map will be created at the missing key. | ||
* Once fully applied, this returns a new tree containing the new node `value` at the location determined by `nodePath`. | ||
* If an equivalent node already exists in this tree it will be replaced. | ||
* If any keys on `nodePath` do not exist, a new Immutable `Map` will be created at the missing key. | ||
* | ||
* Be aware that by setting values on deep nodes that don't yet exist, | ||
* this may cause Maps to be created for child Iterables where other Iterables may be used for children elsewhere in the tree. | ||
* this may cause `Maps` to be created for child `Iterables` where other types of `Iterables` may be used for children elsewhere in the tree. | ||
* This will be addressed in a future release. | ||
* | ||
* When no childPath is provided then this is functionally equivalent to Immutable's setIn function. | ||
* When no `childPath` is provided then this is functionally equivalent to Immutable's `setIn()` method. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node to set. | ||
* @param {NodePath} nodePath A NodePath used to uniquely identify the node to set. | ||
* @param value The value to set the node to. | ||
* @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. | ||
* @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 deepSet(keys, value, childPath) { | ||
return (tree) => tree.setIn(keysToPath(keys, childPath), value); | ||
function deepSet(nodePath, value, childPath) { | ||
return (tree) => tree.setIn(nodePathToKeys(nodePath, childPath), value); | ||
} | ||
/** | ||
* Once fully applied, this returns a new tree where the children of the node at [keys] are set to [value]. | ||
* If no node exists at [keys] then an Immutable Map will be created at that location. | ||
* When no childPath is provided then this is functionally equivalent to Immutable's setIn function. | ||
* Once fully applied, this returns a new tree where the children `Iterable` of the node at `nodePath` are set to `value`. | ||
* If no node exists at `nodePath` then an Immutable `Map` will be created at that location. | ||
* When no `childPath` is provided then this is functionally equivalent to both Immutable's `setIn()` method and `deepSet()`. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node to set. | ||
* @param {NodePath} nodePath A `NodePath` used to uniquely identify a node whose children will be set. | ||
* @param value The value to set the node to. | ||
* @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. | ||
* @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 deepSetChildren(keys, value, childPath) { | ||
return (tree) => tree.setIn(keysToPathChildren(keys, childPath), value); | ||
function deepSetChildren(nodePath, value, childPath) { | ||
return (tree) => tree.setIn(nodePathToKeysChildren(nodePath, childPath), value); | ||
} | ||
@@ -46,0 +46,0 @@ |
@@ -13,2 +13,7 @@ import { | ||
* | ||
* @example | ||
* asList(null); // returns List() | ||
* asList([0,1,2]); // returns List().of(0,1,2) | ||
* asList(List().of(0,1,2)); // returns List().of(0,1,2) | ||
* | ||
* @param {Array|List|null} input The input to be convert to a List. List items will pass through unchanged, all others will be passed into a List constructor. | ||
@@ -23,44 +28,77 @@ * @return {List} The equivalent List. | ||
/** | ||
* Accepts a node and returns a boolean indicating if the node is a leaf node (i.e. it has no children). | ||
* Turns a node's nodePath and its childPath into a full key path. | ||
* | ||
* @param {*} node The node to check. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @return {boolean} A boolean indicating if the node is a leaf node. | ||
* @example | ||
* nodePathToKeys(null, ['children']); // returns [] | ||
* nodePathToKeys(['bob'], ['children']); // returns ['children', 'bob'] | ||
* nodePathToKeys([0,1], ['children']); // returns ['children', 0, 'children', 1] | ||
* | ||
* @param {NodePath} nodePath A NodePath used to uniquely identify a node. | ||
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find each node's children from within each node. | ||
* @return {List} | ||
*/ | ||
function isLeaf(node, childPath = null) { | ||
if(!Iterable.isIterable(node)) { | ||
return true; | ||
} | ||
const children = node.getIn(childPath || []); | ||
return !children || children.isEmpty(); | ||
function nodePathToKeys(nodePath, childPath = null) { | ||
childPath = asList(childPath); | ||
return asList(nodePath).reduce((fullPath, key) => { | ||
return fullPath.concat(childPath).push(key); | ||
}, List()); | ||
} | ||
/** | ||
* Turns a node's keys and its childPath into a full path. | ||
* Turns a node's nodePath and its childPath into a full key path to the node's children. | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @example | ||
* nodePathToKeysChildren(null, ['children']); // returns ['children'] | ||
* nodePathToKeysChildren(['bob'], ['children']); // returns ['children', 'bob', children'] | ||
* nodePathToKeysChildren([0,1], ['children']); // returns ['children', 0, 'children', 1, 'children'] | ||
* | ||
* @param {NodePath} nodePath A NodePath used to uniquely identify a node. | ||
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find each node's children from within each node. | ||
* @return {List} | ||
*/ | ||
function keysToPath(keys, childPath = null) { | ||
function nodePathToKeysChildren(nodePath, childPath = null) { | ||
childPath = asList(childPath); | ||
return keys.reduce((fullPath, key) => { | ||
return fullPath.concat(childPath).push(key); | ||
}, List()); | ||
return childPath.isEmpty() | ||
? nodePathToKeys(nodePath) | ||
: nodePathToKeys(nodePath, childPath).concat(childPath); | ||
} | ||
/** | ||
* Turns a node's keys and its childPath into a full path to the node's children. | ||
* Accepts a node and returns a boolean indicating if the node is a leaf node (i.e. it has no children). | ||
* | ||
* @param {Array|List} keys An {Array} or {List} of keys used to identify a node. | ||
* @param {Array|List} [childPath=null] An {Array} or {List} of the key path to each node's children. | ||
* @return {List} | ||
* @example | ||
* const tree = fromJS({ | ||
* name: "root", | ||
* children: [ | ||
* { | ||
* name: "child 1", | ||
* children: [ | ||
* {name: "grandchild 1"}, | ||
* {name: "grandchild 2"} | ||
* ] | ||
* }, | ||
* { | ||
* name: "child 2", | ||
* children: [] | ||
* } | ||
* ] | ||
* }); | ||
* | ||
* isLeaf(tree.getIn(['children', 0]), ['childPath']); // returns false because child 1 has children | ||
* isLeaf(tree.getIn(['children', 1]), ['childPath']); // returns true because child 2 has no children | ||
* // note that normally deepGet is recommended instead of getIn for getting nodes from a tree | ||
* | ||
* @param {*} node The node to check. | ||
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find each node's children from within each node. | ||
* @return {boolean} A boolean indicating if the node is a leaf node. | ||
*/ | ||
function keysToPathChildren(keys, childPath = null) { | ||
return childPath == null | ||
? asList(keys) | ||
: keysToPath(keys, childPath).concat(childPath); | ||
function isLeaf(node, childPath = null) { | ||
if(!Iterable.isIterable(node)) { | ||
return true; | ||
} | ||
const children = node.getIn(childPath || []); | ||
return !children || children.isEmpty(); | ||
} | ||
@@ -70,5 +108,5 @@ | ||
asList, | ||
isLeaf, | ||
keysToPath, | ||
keysToPathChildren | ||
nodePathToKeys, | ||
nodePathToKeysChildren, | ||
isLeaf | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
191678
1794
65
0