simple-tree-utils
Advanced tools
Comparing version 0.0.7 to 1.0.0
@@ -24,4 +24,13 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.treeUtils = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
exports.TreeUtils = void 0; | ||
/** | ||
* Default name of unique identifier property in nodes | ||
*/ | ||
const DEFAULT_ID_PROP = 'id'; | ||
/** | ||
* Default name of parent identifier property in nodes | ||
*/ | ||
const DEFAULT_PARENT_ID_PROP = 'parentId'; | ||
/** | ||
* Default name of property where child nodes are stored | ||
*/ | ||
const DEFAULT_CHILDREN_PROP = 'children'; | ||
@@ -32,2 +41,6 @@ /** | ||
class TreeUtils { | ||
/** | ||
* Constructor of class | ||
* @param config - to configure class, if configuration option is omitted, default one is used | ||
*/ | ||
constructor(config) { | ||
@@ -40,3 +53,3 @@ this.idProp = (config === null || config === void 0 ? void 0 : config.idProp) || DEFAULT_ID_PROP; | ||
* Convert list to tree like structure | ||
* @param list list of objects (object need to have id and parentId) | ||
* @param list list of objects, objects need to have id (as you configured, or 'id' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId id of parent node | ||
@@ -51,3 +64,3 @@ */ | ||
* Convert tree like structure to list | ||
* @param tree tree of objects (object need to have child property and parentId) | ||
* @param tree tree of objects, objects need to have children (as you configured, or 'children' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId | ||
@@ -65,5 +78,21 @@ */ | ||
} | ||
/** | ||
* Method to find node in tree structure by given id | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns found node | ||
*/ | ||
findTreeNodeById(tree, id) { | ||
return this.findTreeNode(tree, item => item[this.idProp] === id); | ||
} | ||
/** | ||
* Method to find node in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find node | ||
* @returns found node | ||
* @example | ||
* ```ts | ||
* utils.findTreeNode(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findTreeNode(tree, fn) { | ||
@@ -76,2 +105,12 @@ const node = tree.find(item => fn(item)); | ||
} | ||
/** | ||
* Method to find all nodes in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find all nodes | ||
* @returns all found nodes | ||
* @example | ||
* ```ts | ||
* utils.findAllTreeNodes(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findAllTreeNodes(tree, fn) { | ||
@@ -81,2 +120,7 @@ const nodes = tree.filter(item => fn(item)); | ||
} | ||
/** | ||
* Method to delete node in tree by given id (mutable operation!) | ||
* @param tree - tree structure for node deleting | ||
* @param id - identifier of node to delete | ||
*/ | ||
deleteNode(tree, id) { | ||
@@ -89,2 +133,8 @@ const index = tree.findIndex(item => item[this.idProp] == id); | ||
} | ||
/** | ||
* Method to add new node to tree (mutable operation!) | ||
* @param tree - tree structure for node adding | ||
* @param parentId - identifier of parent node, null if new node should be on root level | ||
* @param childData - data of new node | ||
*/ | ||
addNode(tree, parentId, childData) { | ||
@@ -102,2 +152,8 @@ if (parentId == null) { | ||
} | ||
/** | ||
* Method to update node by id with given data in tree (mutable operation!) | ||
* @param tree - tree structure for node editing | ||
* @param id - identifier of node to be updated | ||
* @param data - new data of node (you should also pass children if you want to keep it) | ||
*/ | ||
editNode(tree, id, data) { | ||
@@ -111,2 +167,8 @@ const index = tree.findIndex(item => item[this.idProp] == id); | ||
} | ||
/** | ||
* Method to find all children nodes of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found children nodes | ||
*/ | ||
findAllChildrenNodes(tree, id) { | ||
@@ -119,2 +181,8 @@ const node = this.findTreeNodeById(tree, id); | ||
} | ||
/** | ||
* Helper method to recursively get all children nodes of given node in tree structure | ||
* @param node - we want to get all of its children | ||
* @returns all found children nodes | ||
* @private | ||
*/ | ||
getChildrenNodes(node) { | ||
@@ -126,2 +194,8 @@ return [ | ||
} | ||
/** | ||
* Method to find all parents of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found parent nodes | ||
*/ | ||
findAllParentNodes(tree, id) { | ||
@@ -136,2 +210,9 @@ const parents = []; | ||
} | ||
/** | ||
* Method to find parent of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @param parent - parent node, if we found something (for recursion only) | ||
* @returns found parent node | ||
*/ | ||
findNodeParent(tree, id, parent = null) { | ||
@@ -144,2 +225,8 @@ const node = tree.find(item => item[this.idProp] === id); | ||
} | ||
/** | ||
* Helper method to deep clone object | ||
* @param obj - object to be cloned | ||
* @private | ||
* @returns deep cloned object | ||
*/ | ||
static deepCopy(obj) { | ||
@@ -146,0 +233,0 @@ return JSON.parse(JSON.stringify(obj)); |
@@ -1,4 +0,16 @@ | ||
interface IConfig { | ||
/** | ||
* IConfig interface for configuring whole class during instantiating | ||
*/ | ||
export interface IConfig { | ||
/** | ||
* Name of unique identifier property in nodes | ||
*/ | ||
idProp?: string; | ||
/** | ||
* Name of parent identifier property in nodes | ||
*/ | ||
parentIdProp?: string; | ||
/** | ||
* Name of property where child nodes are stored | ||
*/ | ||
childrenProp?: string; | ||
@@ -10,9 +22,22 @@ } | ||
export declare class TreeUtils { | ||
/** | ||
* Name of unique identifier property in nodes (default value is `id`) | ||
*/ | ||
private readonly idProp; | ||
/** | ||
* Name of parent identifier property in nodes (default value is `parentId`) | ||
*/ | ||
private readonly parentIdProp; | ||
/** | ||
* Name of property where child nodes are stored (default value is `children`) | ||
*/ | ||
private readonly childrenProp; | ||
/** | ||
* Constructor of class | ||
* @param config - to configure class, if configuration option is omitted, default one is used | ||
*/ | ||
constructor(config?: IConfig); | ||
/** | ||
* Convert list to tree like structure | ||
* @param list list of objects (object need to have id and parentId) | ||
* @param list list of objects, objects need to have id (as you configured, or 'id' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId id of parent node | ||
@@ -23,18 +48,91 @@ */ | ||
* Convert tree like structure to list | ||
* @param tree tree of objects (object need to have child property and parentId) | ||
* @param tree tree of objects, objects need to have children (as you configured, or 'children' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId | ||
*/ | ||
tree2List(tree: any[], parentId?: any): any[]; | ||
/** | ||
* Method to find node in tree structure by given id | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns found node | ||
*/ | ||
findTreeNodeById(tree: any[], id: any): any; | ||
/** | ||
* Method to find node in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find node | ||
* @returns found node | ||
* @example | ||
* ```ts | ||
* utils.findTreeNode(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findTreeNode(tree: any[], fn: (item: any) => boolean): any; | ||
/** | ||
* Method to find all nodes in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find all nodes | ||
* @returns all found nodes | ||
* @example | ||
* ```ts | ||
* utils.findAllTreeNodes(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findAllTreeNodes(tree: any[], fn: (item: any) => boolean): any; | ||
/** | ||
* Method to delete node in tree by given id (mutable operation!) | ||
* @param tree - tree structure for node deleting | ||
* @param id - identifier of node to delete | ||
*/ | ||
deleteNode(tree: any[], id: any): any; | ||
/** | ||
* Method to add new node to tree (mutable operation!) | ||
* @param tree - tree structure for node adding | ||
* @param parentId - identifier of parent node, null if new node should be on root level | ||
* @param childData - data of new node | ||
*/ | ||
addNode(tree: any[], parentId: any, childData: any): void; | ||
/** | ||
* Method to update node by id with given data in tree (mutable operation!) | ||
* @param tree - tree structure for node editing | ||
* @param id - identifier of node to be updated | ||
* @param data - new data of node (you should also pass children if you want to keep it) | ||
*/ | ||
editNode(tree: any[], id: any, data: any): void; | ||
/** | ||
* Method to find all children nodes of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found children nodes | ||
*/ | ||
findAllChildrenNodes(tree: any[], id: any): any[]; | ||
/** | ||
* Helper method to recursively get all children nodes of given node in tree structure | ||
* @param node - we want to get all of its children | ||
* @returns all found children nodes | ||
* @private | ||
*/ | ||
private getChildrenNodes; | ||
/** | ||
* Method to find all parents of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found parent nodes | ||
*/ | ||
findAllParentNodes(tree: any[], id: any): any[]; | ||
/** | ||
* Method to find parent of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @param parent - parent node, if we found something (for recursion only) | ||
* @returns found parent node | ||
*/ | ||
findNodeParent(tree: any[], id: any, parent?: any): any; | ||
/** | ||
* Helper method to deep clone object | ||
* @param obj - object to be cloned | ||
* @private | ||
* @returns deep cloned object | ||
*/ | ||
private static deepCopy; | ||
} | ||
export {}; |
@@ -16,4 +16,13 @@ "use strict"; | ||
exports.TreeUtils = void 0; | ||
/** | ||
* Default name of unique identifier property in nodes | ||
*/ | ||
const DEFAULT_ID_PROP = 'id'; | ||
/** | ||
* Default name of parent identifier property in nodes | ||
*/ | ||
const DEFAULT_PARENT_ID_PROP = 'parentId'; | ||
/** | ||
* Default name of property where child nodes are stored | ||
*/ | ||
const DEFAULT_CHILDREN_PROP = 'children'; | ||
@@ -24,2 +33,6 @@ /** | ||
class TreeUtils { | ||
/** | ||
* Constructor of class | ||
* @param config - to configure class, if configuration option is omitted, default one is used | ||
*/ | ||
constructor(config) { | ||
@@ -32,3 +45,3 @@ this.idProp = (config === null || config === void 0 ? void 0 : config.idProp) || DEFAULT_ID_PROP; | ||
* Convert list to tree like structure | ||
* @param list list of objects (object need to have id and parentId) | ||
* @param list list of objects, objects need to have id (as you configured, or 'id' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId id of parent node | ||
@@ -43,3 +56,3 @@ */ | ||
* Convert tree like structure to list | ||
* @param tree tree of objects (object need to have child property and parentId) | ||
* @param tree tree of objects, objects need to have children (as you configured, or 'children' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId | ||
@@ -57,5 +70,21 @@ */ | ||
} | ||
/** | ||
* Method to find node in tree structure by given id | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns found node | ||
*/ | ||
findTreeNodeById(tree, id) { | ||
return this.findTreeNode(tree, item => item[this.idProp] === id); | ||
} | ||
/** | ||
* Method to find node in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find node | ||
* @returns found node | ||
* @example | ||
* ```ts | ||
* utils.findTreeNode(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findTreeNode(tree, fn) { | ||
@@ -68,2 +97,12 @@ const node = tree.find(item => fn(item)); | ||
} | ||
/** | ||
* Method to find all nodes in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find all nodes | ||
* @returns all found nodes | ||
* @example | ||
* ```ts | ||
* utils.findAllTreeNodes(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findAllTreeNodes(tree, fn) { | ||
@@ -73,2 +112,7 @@ const nodes = tree.filter(item => fn(item)); | ||
} | ||
/** | ||
* Method to delete node in tree by given id (mutable operation!) | ||
* @param tree - tree structure for node deleting | ||
* @param id - identifier of node to delete | ||
*/ | ||
deleteNode(tree, id) { | ||
@@ -81,2 +125,8 @@ const index = tree.findIndex(item => item[this.idProp] == id); | ||
} | ||
/** | ||
* Method to add new node to tree (mutable operation!) | ||
* @param tree - tree structure for node adding | ||
* @param parentId - identifier of parent node, null if new node should be on root level | ||
* @param childData - data of new node | ||
*/ | ||
addNode(tree, parentId, childData) { | ||
@@ -94,2 +144,8 @@ if (parentId == null) { | ||
} | ||
/** | ||
* Method to update node by id with given data in tree (mutable operation!) | ||
* @param tree - tree structure for node editing | ||
* @param id - identifier of node to be updated | ||
* @param data - new data of node (you should also pass children if you want to keep it) | ||
*/ | ||
editNode(tree, id, data) { | ||
@@ -103,2 +159,8 @@ const index = tree.findIndex(item => item[this.idProp] == id); | ||
} | ||
/** | ||
* Method to find all children nodes of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found children nodes | ||
*/ | ||
findAllChildrenNodes(tree, id) { | ||
@@ -111,2 +173,8 @@ const node = this.findTreeNodeById(tree, id); | ||
} | ||
/** | ||
* Helper method to recursively get all children nodes of given node in tree structure | ||
* @param node - we want to get all of its children | ||
* @returns all found children nodes | ||
* @private | ||
*/ | ||
getChildrenNodes(node) { | ||
@@ -118,2 +186,8 @@ return [ | ||
} | ||
/** | ||
* Method to find all parents of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found parent nodes | ||
*/ | ||
findAllParentNodes(tree, id) { | ||
@@ -128,2 +202,9 @@ const parents = []; | ||
} | ||
/** | ||
* Method to find parent of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @param parent - parent node, if we found something (for recursion only) | ||
* @returns found parent node | ||
*/ | ||
findNodeParent(tree, id, parent = null) { | ||
@@ -136,2 +217,8 @@ const node = tree.find(item => item[this.idProp] === id); | ||
} | ||
/** | ||
* Helper method to deep clone object | ||
* @param obj - object to be cloned | ||
* @private | ||
* @returns deep cloned object | ||
*/ | ||
static deepCopy(obj) { | ||
@@ -138,0 +225,0 @@ return JSON.parse(JSON.stringify(obj)); |
{ | ||
"name": "simple-tree-utils", | ||
"version": "0.0.7", | ||
"version": "1.0.0", | ||
"description": "Tree utils library.", | ||
@@ -17,3 +17,4 @@ "keywords": [], | ||
"lint:fix": "npx eslint ./src --fix", | ||
"docs": "npx typedoc src/index.ts", | ||
"docs": "npx typedoc src/main.ts", | ||
"deploy": "npm run docs && netlify deploy --dir=docs --prod", | ||
"publish": "npm run build && npm publish" | ||
@@ -20,0 +21,0 @@ }, |
103
README.md
@@ -1,11 +0,104 @@ | ||
# Tree utils | ||
Tree utils library. | ||
[![npm version](https://badge.fury.io/js/simple-tree-utils.svg)](https://badge.fury.io/js/simple-tree-utils) | ||
[![CircleCI](https://circleci.com/gh/Raiper34/simple-tree-utils.svg?style=shield)](https://circleci.com/gh/Raiper34/simple-tree-utils) | ||
[![Coverage Status](https://coveralls.io/repos/github/Raiper34/simple-tree-utils/badge.svg?branch=main)](https://coveralls.io/github/Raiper34/simple-tree-utils?branch=main) | ||
![npm bundle size](https://img.shields.io/bundlephobia/min/simple-tree-utils) | ||
![NPM](https://img.shields.io/npm/l/simple-tree-utils) | ||
[![docs](https://badgen.net/badge/docs/online/orange)](https://simple-tree-utils.netlify.app) | ||
[![](https://data.jsdelivr.com/v1/package/npm/simple-tree-utils/badge?style=rounded)](https://www.jsdelivr.com/package/npm/simple-tree-utils) | ||
# Simple Tree Utils | ||
Simple Tree Utils is the library to convert and manipulate with tree-like structures. | ||
Library provides converter from an array of objects to trees like structure and vice versa, | ||
and also methods to manipulate that tree and/or search in that tree. | ||
It is created because I needed to convert the array of objects into a tree to visualize in | ||
[**Angular tree component**](https://www.npmjs.com/package/@circlon/angular-tree-component). | ||
Surely there are plenty of similar libraries, but I think all of them work with their own | ||
models/classes, I needed to use my own data without no extra instantiating, | ||
or adding extra methods/properties into the working model. | ||
# Instalation | ||
`npm install simple-tree-utils --save` | ||
Install library using npm `npm install simple-tree-utils --save` and import main class | ||
into your code base `import {TreeUtils} from 'simple-tree-utils';` . | ||
# Usage | ||
todo | ||
First instantiate class with config, or without config. | ||
```ts | ||
const treeUtils = new TreeUtils(); // without config, default values are used (id as idProp, parentId as parentIdProp, children as childrenProp) | ||
const treeUtils2 = new TreeUtils({ | ||
idProp: 'id', // the key of a unique identifier for an object (source object) | ||
parentIdProp: 'parentId', // the key of a unique parent identifier (source object) | ||
childrenProp: 'children', // the key, where child nodes are stored (destination object tree) | ||
}); | ||
``` | ||
After instantiation, you can use tree utils. You can convert the array of the following objects into a tree using `list2Tree` method as following | ||
```ts | ||
const items = [ | ||
{id: 1, parentId: null, name: 'Node 1'}, | ||
{id: 2, parentId: null, name: 'Node 2'}, | ||
{id: 3, parentId: 1, name: 'Node 3'}, | ||
{id: 4, parentId: 1, name: 'Node 4'}, | ||
{id: 5, parentId: 2, name: 'Node 5'}, | ||
{id: 6, parentId: 3, name: 'Node 6'}, | ||
]; | ||
const output = treeUtils.list2Tree(items); | ||
``` | ||
then the output of lift2Tree will be | ||
```ts | ||
const tree = [ | ||
{ | ||
id: 1, parentId: null, name: 'Node 1', children: [ | ||
{ | ||
id: 3, parentId: 1, name: 'Node 3', children: [ | ||
{id: 6, parentId: 3, name: 'Node 6', children: []}, | ||
] | ||
}, | ||
{id: 4, parentId: 1, name: 'Node 4', children: []}, | ||
] | ||
}, | ||
{ | ||
id: 2, parentId: null, name: 'Node 2', children: [ | ||
{id: 5, parentId: 2, name: 'Node 5', children: []}, | ||
] | ||
}, | ||
]; | ||
``` | ||
When you got a structure like the above, you can use all the following methods: | ||
| **Name** | Desription | Signature | | ||
| ---- | ---------- | --------- | | ||
| **addNode** | Method to add new node to tree (mutable operation!) | `addNode(tree: any[], parentId: any, childData: any): void` | | ||
| **deleteNode** | Method to delete node in tree by given id (mutable operation!) | `deleteNode(tree: any[], id: any): any` | | ||
| **editNode** | Method to update node by id with given data in tree (mutable operation!) | `editNode(tree: any[], id: any, data: any): void` | | ||
| **findAllChildrenNodes** | Method to find all children nodes of given node in tree structure | `findAllChildrenNodes(tree: any[], id: any): any[]` | | ||
| **findAllParentNodes** | Method to find all parents of given node in tree structure | `findAllParentNodes(tree: any[], id: any): any[]` | | ||
| **findAllTreeNodes** | Method to find all nodes in tree structure by given callback function | `findAllTreeNodes(tree: any[], fn: ((item: any) => boolean)): any` | | ||
| **findNodeParent** | Method to find parent of given node in tree structure | `findNodeParent(tree: any[], id: any, parent?: any): any` | | ||
| **findTreeNode** | Method to find node in tree structure by given callback function | `findTreeNode(tree: any[], fn: ((item: any) => boolean)): any` | | ||
| **findTreeNodeById** | Method to find node in tree structure by given id | `findTreeNodeById(tree: any[], id: any): any` | | ||
For example, we can find node by giving callback | ||
```ts | ||
const node = treeUtils.findTreeNode(tree, item => item.id === 2); | ||
``` | ||
If you need a list again, you can convert the tree back to a list using `treeUtils.tree2List` method | ||
```ts | ||
treeUtils.tree2List(tree); | ||
``` | ||
# Documentation | ||
For more details and complete documentation check: https://simple-tree-utils.netlify.app/ | ||
# Usage in browser | ||
You can also use this library in the browser without compiling using jsDelivr. | ||
Import script into HTML file, and you can access classes through the global `treeUtils` object. | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/simple-tree-utils@1/dist/browser-bundle.min.js"></script> | ||
<script> | ||
const utils = new treeUtils.TreeUtils(); | ||
const tree = utils.list2Tree(items); | ||
</script> | ||
``` | ||
# License | ||
MIT | ||
MIT |
114
src/main.ts
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
interface IConfig { | ||
/** | ||
* IConfig interface for configuring whole class during instantiating | ||
*/ | ||
export interface IConfig { | ||
/** | ||
* Name of unique identifier property in nodes | ||
*/ | ||
idProp?: string, | ||
/** | ||
* Name of parent identifier property in nodes | ||
*/ | ||
parentIdProp?: string, | ||
/** | ||
* Name of property where child nodes are stored | ||
*/ | ||
childrenProp?: string, | ||
} | ||
/** | ||
* Default name of unique identifier property in nodes | ||
*/ | ||
const DEFAULT_ID_PROP = 'id'; | ||
/** | ||
* Default name of parent identifier property in nodes | ||
*/ | ||
const DEFAULT_PARENT_ID_PROP = 'parentId'; | ||
/** | ||
* Default name of property where child nodes are stored | ||
*/ | ||
const DEFAULT_CHILDREN_PROP = 'children'; | ||
@@ -18,6 +39,19 @@ | ||
/** | ||
* Name of unique identifier property in nodes (default value is `id`) | ||
*/ | ||
private readonly idProp: string; | ||
/** | ||
* Name of parent identifier property in nodes (default value is `parentId`) | ||
*/ | ||
private readonly parentIdProp: string; | ||
/** | ||
* Name of property where child nodes are stored (default value is `children`) | ||
*/ | ||
private readonly childrenProp: string; | ||
/** | ||
* Constructor of class | ||
* @param config - to configure class, if configuration option is omitted, default one is used | ||
*/ | ||
constructor(config?: IConfig) { | ||
@@ -31,3 +65,3 @@ this.idProp = config?.idProp || DEFAULT_ID_PROP; | ||
* Convert list to tree like structure | ||
* @param list list of objects (object need to have id and parentId) | ||
* @param list list of objects, objects need to have id (as you configured, or 'id' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId id of parent node | ||
@@ -46,3 +80,3 @@ */ | ||
* Convert tree like structure to list | ||
* @param tree tree of objects (object need to have child property and parentId) | ||
* @param tree tree of objects, objects need to have children (as you configured, or 'children' by default) and parentId property (as you configured, or 'parentId' by default) | ||
* @param parentId | ||
@@ -61,2 +95,8 @@ */ | ||
/** | ||
* Method to find node in tree structure by given id | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns found node | ||
*/ | ||
findTreeNodeById(tree: any[], id: any): any { | ||
@@ -66,2 +106,12 @@ return this.findTreeNode(tree, item => item[this.idProp] === id); | ||
/** | ||
* Method to find node in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find node | ||
* @returns found node | ||
* @example | ||
* ```ts | ||
* utils.findTreeNode(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findTreeNode(tree: any[], fn: (item: any) => boolean): any { | ||
@@ -77,2 +127,12 @@ const node = tree.find(item => fn(item)); | ||
/** | ||
* Method to find all nodes in tree structure by given callback function | ||
* @param tree - tree structure to search in | ||
* @param fn - callback function to find all nodes | ||
* @returns all found nodes | ||
* @example | ||
* ```ts | ||
* utils.findAllTreeNodes(tree, item => item.id === myId); | ||
* ``` | ||
*/ | ||
findAllTreeNodes(tree: any[], fn: (item: any) => boolean): any { | ||
@@ -85,2 +145,7 @@ const nodes = tree.filter(item => fn(item)); | ||
/** | ||
* Method to delete node in tree by given id (mutable operation!) | ||
* @param tree - tree structure for node deleting | ||
* @param id - identifier of node to delete | ||
*/ | ||
deleteNode(tree: any[], id: any): any { | ||
@@ -94,2 +159,8 @@ const index = tree.findIndex(item => item[this.idProp] == id); | ||
/** | ||
* Method to add new node to tree (mutable operation!) | ||
* @param tree - tree structure for node adding | ||
* @param parentId - identifier of parent node, null if new node should be on root level | ||
* @param childData - data of new node | ||
*/ | ||
addNode(tree: any[], parentId: any, childData: any): void { | ||
@@ -108,2 +179,8 @@ if (parentId == null) { | ||
/** | ||
* Method to update node by id with given data in tree (mutable operation!) | ||
* @param tree - tree structure for node editing | ||
* @param id - identifier of node to be updated | ||
* @param data - new data of node (you should also pass children if you want to keep it) | ||
*/ | ||
editNode(tree: any[], id: any, data: any): void { | ||
@@ -118,2 +195,8 @@ const index = tree.findIndex(item => item[this.idProp] == id); | ||
/** | ||
* Method to find all children nodes of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found children nodes | ||
*/ | ||
findAllChildrenNodes(tree: any[], id: any): any[] { | ||
@@ -127,2 +210,8 @@ const node = this.findTreeNodeById(tree, id); | ||
/** | ||
* Helper method to recursively get all children nodes of given node in tree structure | ||
* @param node - we want to get all of its children | ||
* @returns all found children nodes | ||
* @private | ||
*/ | ||
private getChildrenNodes(node: any): any[] { | ||
@@ -135,2 +224,8 @@ return [ | ||
/** | ||
* Method to find all parents of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @returns all found parent nodes | ||
*/ | ||
findAllParentNodes(tree: any[], id: any): any[] { | ||
@@ -146,2 +241,9 @@ const parents = []; | ||
/** | ||
* Method to find parent of given node in tree structure | ||
* @param tree - tree structure to search in | ||
* @param id - identifier of node | ||
* @param parent - parent node, if we found something (for recursion only) | ||
* @returns found parent node | ||
*/ | ||
findNodeParent(tree: any[], id: any, parent: any = null): any { | ||
@@ -157,2 +259,8 @@ const node = tree.find(item => item[this.idProp] === id); | ||
/** | ||
* Helper method to deep clone object | ||
* @param obj - object to be cloned | ||
* @private | ||
* @returns deep cloned object | ||
*/ | ||
private static deepCopy(obj: any): any { | ||
@@ -159,0 +267,0 @@ return JSON.parse(JSON.stringify(obj)); |
@@ -5,8 +5,8 @@ import {TreeUtils} from "../src"; | ||
const LIST_MOCK_ITEMS = [ | ||
{id: 1, parentId: null, name: 'Node 1'}, | ||
{id: 2, parentId: null, name: 'Node 2'}, | ||
{id: 3, parentId: 1, name: 'Node 3'}, | ||
{id: 4, parentId: 1, name: 'Node 4'}, | ||
{id: 5, parentId: 2, name: 'Node 5'}, | ||
{id: 6, parentId: 3, name: 'Node 6'}, | ||
{customId: 1, parentCustomId: null, name: 'Node 1'}, | ||
{customId: 2, parentCustomId: null, name: 'Node 2'}, | ||
{customId: 3, parentCustomId: 1, name: 'Node 3'}, | ||
{customId: 4, parentCustomId: 1, name: 'Node 4'}, | ||
{customId: 5, parentCustomId: 2, name: 'Node 5'}, | ||
{customId: 6, parentCustomId: 3, name: 'Node 6'}, | ||
]; | ||
@@ -16,14 +16,14 @@ | ||
{ | ||
id: 1, parentId: null, name: 'Node 1', children: [ | ||
customId: 1, parentCustomId: null, name: 'Node 1', customChildren: [ | ||
{ | ||
id: 3, parentId: 1, name: 'Node 3', children: [ | ||
{id: 6, parentId: 3, name: 'Node 6', children: []}, | ||
customId: 3, parentCustomId: 1, name: 'Node 3', customChildren: [ | ||
{customId: 6, parentCustomId: 3, name: 'Node 6', customChildren: []}, | ||
] | ||
}, | ||
{id: 4, parentId: 1, name: 'Node 4', children: []}, | ||
{customId: 4, parentCustomId: 1, name: 'Node 4', customChildren: []}, | ||
] | ||
}, | ||
{ | ||
id: 2, parentId: null, name: 'Node 2', children: [ | ||
{id: 5, parentId: 2, name: 'Node 5', children: []}, | ||
customId: 2, parentCustomId: null, name: 'Node 2', customChildren: [ | ||
{customId: 5, parentCustomId: 2, name: 'Node 5', customChildren: []}, | ||
] | ||
@@ -33,6 +33,16 @@ }, | ||
const treeUtils = new TreeUtils(); | ||
const treeUtils = new TreeUtils({ | ||
idProp: 'customId', | ||
parentIdProp: 'parentCustomId', | ||
childrenProp: 'customChildren', | ||
}); | ||
describe('Tree utils methods', () => { | ||
it('should setup converter with defaults when parameter is not provided', () => { | ||
const treeUtils = new TreeUtils(); | ||
expect(treeUtils.list2Tree([{id: 1, parentId: null, name: 'Node 1'}])) | ||
.toEqual([{id: 1, parentId: null, name: 'Node 1', children: []}]); | ||
}); | ||
it('should convert list to tree', () => { | ||
@@ -43,10 +53,18 @@ expect(treeUtils.list2Tree(LIST_MOCK_ITEMS)).toEqual(TREE_MOCK_ITEMS); | ||
it('should convert tree to list', () => { | ||
expect(treeUtils.tree2List(TREE_MOCK_ITEMS).sort((a: any, b: any) => a.id - b.id)).toEqual(LIST_MOCK_ITEMS); | ||
expect(treeUtils.tree2List(TREE_MOCK_ITEMS).sort((a: any, b: any) => a.customId - b.customId)).toEqual(LIST_MOCK_ITEMS); | ||
}); | ||
it('should find node by given id', () => { | ||
expect(treeUtils.findTreeNodeById(TREE_MOCK_ITEMS, 6)).toEqual({id: 6, parentId: 3, name: 'Node 6', children: []}); | ||
it('should find node by given customId', () => { | ||
expect(treeUtils.findTreeNodeById(TREE_MOCK_ITEMS, 6)).toEqual({customId: 6, parentCustomId: 3, name: 'Node 6', customChildren: []}); | ||
}); | ||
it('should delete node by given id', () => { | ||
it('should not fail, when children prop is missing', () => { | ||
const input = [ | ||
{customId: 1, parentCustomId: null, name: 'Node 1'} | ||
] | ||
expect(treeUtils.findTreeNodeById(input, 6)).toBeNull(); | ||
expect(treeUtils.findNodeParent(input, 6)).toBeNull(); | ||
}); | ||
it('should delete node by given customId', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
@@ -56,12 +74,12 @@ treeUtils.deleteNode(mock, 6); | ||
{ | ||
id: 1, parentId: null, name: 'Node 1', children: [ | ||
customId: 1, parentCustomId: null, name: 'Node 1', customChildren: [ | ||
{ | ||
id: 3, parentId: 1, name: 'Node 3', children: [] | ||
customId: 3, parentCustomId: 1, name: 'Node 3', customChildren: [] | ||
}, | ||
{id: 4, parentId: 1, name: 'Node 4', children: []}, | ||
{customId: 4, parentCustomId: 1, name: 'Node 4', customChildren: []}, | ||
] | ||
}, | ||
{ | ||
id: 2, parentId: null, name: 'Node 2', children: [ | ||
{id: 5, parentId: 2, name: 'Node 5', children: []}, | ||
customId: 2, parentCustomId: null, name: 'Node 2', customChildren: [ | ||
{customId: 5, parentCustomId: 2, name: 'Node 5', customChildren: []}, | ||
] | ||
@@ -73,15 +91,15 @@ }, | ||
it('should add node to parent with given id', () => { | ||
it('should add node to parent with given customId', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
treeUtils.addNode(mock, 4, {id: 7, parentId: 4, name: 'Node 7', children: []}); | ||
treeUtils.addNode(mock, 4, {customId: 7, parentCustomId: 4, name: 'Node 7', customChildren: []}); | ||
const out = [ | ||
{ | ||
id: 1, parentId: null, name: 'Node 1', children: [ | ||
customId: 1, parentCustomId: null, name: 'Node 1', customChildren: [ | ||
{ | ||
id: 3, parentId: 1, name: 'Node 3', children: [ | ||
{id: 6, parentId: 3, name: 'Node 6', children: []} | ||
customId: 3, parentCustomId: 1, name: 'Node 3', customChildren: [ | ||
{customId: 6, parentCustomId: 3, name: 'Node 6', customChildren: []} | ||
] | ||
}, | ||
{id: 4, parentId: 1, name: 'Node 4', children: [ | ||
{id: 7, parentId: 4, name: 'Node 7', children: []} | ||
{customId: 4, parentCustomId: 1, name: 'Node 4', customChildren: [ | ||
{customId: 7, parentCustomId: 4, name: 'Node 7', customChildren: []} | ||
]}, | ||
@@ -91,4 +109,4 @@ ] | ||
{ | ||
id: 2, parentId: null, name: 'Node 2', children: [ | ||
{id: 5, parentId: 2, name: 'Node 5', children: []}, | ||
customId: 2, parentCustomId: null, name: 'Node 2', customChildren: [ | ||
{customId: 5, parentCustomId: 2, name: 'Node 5', customChildren: []}, | ||
] | ||
@@ -100,21 +118,22 @@ }, | ||
it('should edit node of given id by given data', () => { | ||
it('should add node to root', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
treeUtils.editNode(mock, 5, {id: 5, parentId: 2, name: 'Node 5 edited', children: []}); | ||
treeUtils.addNode(mock, null, {customId: 7, parentCustomId: 4, name: 'Node 7', customChildren: []}); | ||
const out = [ | ||
{ | ||
id: 1, parentId: null, name: 'Node 1', children: [ | ||
customId: 1, parentCustomId: null, name: 'Node 1', customChildren: [ | ||
{ | ||
id: 3, parentId: 1, name: 'Node 3', children: [ | ||
{id: 6, parentId: 3, name: 'Node 6', children: []} | ||
customId: 3, parentCustomId: 1, name: 'Node 3', customChildren: [ | ||
{customId: 6, parentCustomId: 3, name: 'Node 6', customChildren: []} | ||
] | ||
}, | ||
{id: 4, parentId: 1, name: 'Node 4', children: []}, | ||
{customId: 4, parentCustomId: 1, name: 'Node 4', customChildren: []}, | ||
] | ||
}, | ||
{ | ||
id: 2, parentId: null, name: 'Node 2', children: [ | ||
{id: 5, parentId: 2, name: 'Node 5 edited', children: []}, | ||
customId: 2, parentCustomId: null, name: 'Node 2', customChildren: [ | ||
{customId: 5, parentCustomId: 2, name: 'Node 5', customChildren: []}, | ||
] | ||
}, | ||
{customId: 7, parentCustomId: 4, name: 'Node 7', customChildren: []} | ||
] | ||
@@ -124,5 +143,28 @@ expect(mock).toEqual(out); | ||
it('should edit node of given customId by given data', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
treeUtils.editNode(mock, 5, {customId: 5, parentCustomId: 2, name: 'Node 5 edited', customChildren: []}); | ||
const out = [ | ||
{ | ||
customId: 1, parentCustomId: null, name: 'Node 1', customChildren: [ | ||
{ | ||
customId: 3, parentCustomId: 1, name: 'Node 3', customChildren: [ | ||
{customId: 6, parentCustomId: 3, name: 'Node 6', customChildren: []} | ||
] | ||
}, | ||
{customId: 4, parentCustomId: 1, name: 'Node 4', customChildren: []}, | ||
] | ||
}, | ||
{ | ||
customId: 2, parentCustomId: null, name: 'Node 2', customChildren: [ | ||
{customId: 5, parentCustomId: 2, name: 'Node 5 edited', customChildren: []}, | ||
] | ||
}, | ||
] | ||
expect(mock).toEqual(out); | ||
}); | ||
it('should find parent of node', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
expect(treeUtils.findNodeParent(mock, 6).id).toBe(3); | ||
expect(treeUtils.findNodeParent(mock, 6).customId).toBe(3); | ||
expect(treeUtils.findNodeParent(mock, 1)).toBeNull(); | ||
@@ -133,21 +175,26 @@ }); | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
expect(treeUtils.findAllParentNodes(mock, 6).map((item: any) => item.id)).toEqual([1, 3]); | ||
expect(treeUtils.findAllParentNodes(mock, 4).map((item: any) => item.id)).toEqual([1]); | ||
expect(treeUtils.findAllParentNodes(mock, 5).map((item: any) => item.id)).toEqual([2]); | ||
expect(treeUtils.findAllParentNodes(mock, 1).map((item: any) => item.id)).toEqual([]); | ||
expect(treeUtils.findAllParentNodes(mock, 6).map((item: any) => item.customId)).toEqual([1, 3]); | ||
expect(treeUtils.findAllParentNodes(mock, 4).map((item: any) => item.customId)).toEqual([1]); | ||
expect(treeUtils.findAllParentNodes(mock, 5).map((item: any) => item.customId)).toEqual([2]); | ||
expect(treeUtils.findAllParentNodes(mock, 1).map((item: any) => item.customId)).toEqual([]); | ||
}); | ||
it('should find ALL children of node', () => { | ||
it('should find ALL customChildren of node', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
expect(treeUtils.findAllChildrenNodes(mock, 1).map((item: any) => item.id)).toEqual([3, 4, 6]); | ||
expect(treeUtils.findAllChildrenNodes(mock, 2).map((item: any) => item.id)).toEqual([5]); | ||
expect(treeUtils.findAllChildrenNodes(mock, 5).map((item: any) => item.id)).toEqual([]); | ||
expect(treeUtils.findAllChildrenNodes(mock, 1).map((item: any) => item.customId)).toEqual([3, 4, 6]); | ||
expect(treeUtils.findAllChildrenNodes(mock, 2).map((item: any) => item.customId)).toEqual([5]); | ||
expect(treeUtils.findAllChildrenNodes(mock, 5).map((item: any) => item.customId)).toEqual([]); | ||
}); | ||
it('should not find ALL customChildren of nonexisting node', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
expect(treeUtils.findAllChildrenNodes(mock, 666).map((item: any) => item.customId)).toEqual([]); | ||
}); | ||
it('should find ALL nodes that match callback', () => { | ||
const mock = JSON.parse(JSON.stringify(TREE_MOCK_ITEMS)); | ||
expect(treeUtils.findAllTreeNodes(mock, item => item.id % 2 === 0).map((item: any) => item.id)).toEqual([2, 4, 6]); | ||
expect(treeUtils.findAllTreeNodes(mock, item => item.id % 3 === 0).map((item: any) => item.id)).toEqual([3, 6]); | ||
expect(treeUtils.findAllTreeNodes(mock, item => item.name.includes('Node')).map((item: any) => item.id)).toEqual([1, 2, 3, 4, 6, 5]); | ||
expect(treeUtils.findAllTreeNodes(mock, item => item.customId % 2 === 0).map((item: any) => item.customId)).toEqual([2, 4, 6]); | ||
expect(treeUtils.findAllTreeNodes(mock, item => item.customId % 3 === 0).map((item: any) => item.customId)).toEqual([3, 6]); | ||
expect(treeUtils.findAllTreeNodes(mock, item => item.name.includes('Node')).map((item: any) => item.customId)).toEqual([1, 2, 3, 4, 6, 5]); | ||
}); | ||
}); |
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
62974
17
1096
1
104