import {
Iterable,
List,
Set
} from 'immutable';
import {
deepPick
} from './pick';
/**
* @module utils
*/
/**
* Accepts an `Array`, `List` or `null` and returns an equivalent `List`. Passing in `null` will return an empty `List`.
*
* @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.
* @return {List} The equivalent `List`.
*/
function asList(input) {
return List.isList(input) ? input : List(input);
}
/**
* Accepts a node and returns a copy of it with all children and all non-`childPath` keys removed.
* Essentially the minimum data structure required for the node.
*
* @param {*} node The node to make blank.
* @param {ChildPath} [childPath=null] An Array or List of keys indicating where to find the node's children.
* @return {*} The blank node.
*/
function blankNode(node, childPath = null) {
childPath = asList(childPath);
return node.update(deepPick(childPath));
}
/**
* Turns a node's `nodePath` and its `childPath` into a full key path.
*
* @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 nodePathToKeys(nodePath, childPath = null) {
childPath = asList(childPath);
return asList(nodePath).reduce((fullPath, key) => {
return fullPath.concat(childPath).push(key);
}, List());
}
/**
* Turns a node's nodePath and its childPath into a full key path to the 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 nodePathToKeysChildren(nodePath, childPath = null) {
childPath = asList(childPath);
return childPath.isEmpty()
? nodePathToKeys(nodePath)
: nodePathToKeys(nodePath, childPath).concat(childPath);
}
/**
* Accepts a node and returns a boolean indicating if the node is a leaf node (i.e. it has no children).
*
* @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 isLeaf(node, childPath = null) {
if(!Iterable.isIterable(node)) {
return true;
}
const children = node.getIn(childPath || []);
return !children || children.isEmpty();
}
/**
* Filter an Immutable `Iterable` so it only retains any keys provided in the `keys` param.
* Similar to the pick function in Lodash.
*
* @example
* const map = Map({
* a: "A",
* b: "B",
* c: "C",
* d: "D"
* });
*
* pick(map, ['a', 'c']); // returns Map({a: "A", c: "C"});
*
* @param {Iterable} iterable The `Iterable` to filter.
* @param {Array} keys The iterable's keys to keep.
* @return {Iterable}
*/
function pick(iterable, keys) {
const keySet = Set(keys);
return iterable.filter((v, k) => keySet.has(k));
}
export {
asList,
blankNode,
nodePathToKeys,
nodePathToKeysChildren,
isLeaf,
pick
}