Skip to content

Instantly share code, notes, and snippets.

@baetheus
Last active April 8, 2018 19:24
Show Gist options
  • Select an option

  • Save baetheus/71cf0ec41823b1151237ac6870e18b1a to your computer and use it in GitHub Desktop.

Select an option

Save baetheus/71cf0ec41823b1151237ac6870e18b1a to your computer and use it in GitHub Desktop.
Tree Utilities
import { map, filter, reduce, assign } from 'lodash';
/**
* Map over a tree from the bottom-most nodes up. Applies transformation to deepest children,
* then to parents, and so on. You can even rename the property that contains the children.
*
* Usage:
* tmap(
* x => ({ ...x, value: x.value + 1 || 0 }),
* 'children',
* [{value: 0, children: []}, {children: [{value: 5, children: [{value: 100}]}]}]
* )
* // This will increment all values by 1 or initialize value to 0.
*/
export const tmap = <T extends { [key: string]: T[] | any }, R>(
transformation: (t: T) => R,
children: keyof T,
nodes: T[],
): R[] => {
return map(nodes, node => {
if (node[children] !== undefined) {
const newNode = assign({}, node, { [children]: tmap<T, R>(transformation, children, node[children]) });
return transformation(newNode);
}
return transformation(node);
});
};
/**
* Filter over a tree from bottom-most nodes up. Applies predicate to deepest children,
* then to parents, and so on. So rolling up empty children arrays is possible.
*/
export const tfilter = <T extends { [key: string]: T[] | any }>(
predicate: (t: T) => boolean,
children: keyof T,
nodes: T[],
): T[] => {
if (Array.isArray(nodes)) {
return reduce(
nodes,
(acc, node) => {
if (node[children] !== undefined) {
const newNode = assign({}, node, { [children]: tfilter<T>(predicate, children, node[children]) });
return predicate(newNode) ? [...acc, newNode] : acc;
}
return predicate(node) ? [...acc, node] : acc;
},
<T[]>[],
);
}
return nodes;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment