A TypeScript utility library for converting a flat array structure into a hierarchical tree structure based on parent-child relationships.
This project is based on alferov/array-to-tree, with modernizations, feature enhancements, and bug fixes applied to the original code.
Important
This package has not yet reached version 1.0. Bugs or breaking changes may occur.
- Modern: Written in modern TypeScript and ES for better type support.
- Functional: Adopts a functional programming paradigm, does not mutate the original data, and avoids side effects.
- Configurable: Allows customization of field names and other behaviors to adapt to different data structures.
# Using pnpm
pnpm add @okutils/array-to-tree
# Using yarn
yarn add @okutils/array-to-tree
# Using npm
npm install @okutils/array-to-tree
# Using Bun
bun add @okutils/array-to-tree
import { arrayToTree } from "@okutils/array-to-tree";
const data = [
{ id: 1, name: "A", parentId: null },
{ id: 2, name: "B", parentId: 1 },
{ id: 3, name: "C", parentId: 1 },
{ id: 4, name: "D", parentId: 2 },
{ id: 5, name: "E", parentId: null },
];
const tree = arrayToTree(data);
console.log(tree);
Output:
[
{
"id": 1,
"name": "A",
"parentId": null,
"children": [
{
"id": 2,
"name": "B",
"parentId": 1,
"children": [
{
"id": 4,
"name": "D",
"parentId": 2
}
]
},
{
"id": 3,
"name": "C",
"parentId": 1
}
]
},
{
"id": 5,
"name": "E",
"parentId": null
}
]
arrayToTree(data, options?)
data
:Record<string, any>[]
- A flat array containing the data.options
:ArrayToTreeOptions
(optional) - A configuration options object.
You can pass an options
object to customize field names and other behaviors.
// src/types/options.ts
export interface ArrayToTreeOptions {
childrenId?: string;
customId?: string;
parentId?: string;
rootId?: string;
allowSelfAsParent?: boolean;
}
Option | Type | Default | Description |
---|---|---|---|
customId |
string |
'id' |
Specifies the field name for the unique identifier of a node. |
parentId |
string |
'parentId' |
Specifies the field name for associating with the parent node. |
childrenId |
string |
'children' |
Specifies the field name for the array of child nodes in the generated tree. |
allowSelfAsParent |
boolean |
false |
Whether to allow a node's parentId to be equal to its own id . |
rootId |
string |
'__ARRAY_TO_TREE_VIRTUAL_ROOT_ID__' |
An internal virtual root ID, which usually does not need to be changed. The parentId of root nodes should be null or undefined . |
import { arrayToTree } from "@okutils/array-to-tree";
const data = [
{ key: "node-1", parent: null, title: "Node 1" },
{ key: "node-2", parent: "node-1", title: "Node 2" },
];
const tree = arrayToTree(data, {
customId: "key",
parentId: "parent",
childrenId: "nodes",
});
[
{
"key": "node-1",
"parent": null,
"title": "Node 1",
"nodes": [
{
"key": "node-2",
"parent": "node-1",
"title": "Node 2"
}
]
}
]
Represents an error that occurs when converting an array to a tree structure.
export class ArrayToTreeError extends Error {
constructor(message: string) {
super(message);
this.name = "ArrayToTreeError";
}
}
ArrayToTreeError
is thrown in the following situations:
-
Invalid Input Type: When the
data
argument passed is not an array.arrayToTree("not array"); // Throws ArrayToTreeError: Expected an array but got an invalid argument.
-
Duplicate Node ID: When there are nodes with the same
id
in the array.const data = [ { id: 1, name: "A", parentId: null }, { id: 1, name: "A-duplicate", parentId: null }, // Duplicate id ]; arrayToTree(data); // Throws ArrayToTreeError: Duplicate node id "1" detected.
-
Self-Reference Error: When a node's
parentId
is equal to its ownid
, and theallowSelfAsParent
option isfalse
.const data = [ { id: 1, name: "A", parentId: 1 }, // Self-reference ]; arrayToTree(data, { allowSelfAsParent: false }); // Throws ArrayToTreeError: Node "1" cannot be its own parent (self reference found).
-
Circular Reference Detection: When a circular dependency is formed between nodes.
const data = [ { id: 1, name: "A", parentId: 3 }, { id: 2, name: "B", parentId: 1 }, { id: 3, name: "C", parentId: 2 }, // Forms a cycle: 1 -> 3 -> 2 -> 1 ]; arrayToTree(data); // Throws ArrayToTreeError: Cycle detected in parent chain: 1 -> 3 -> 2 -> 1
It is recommended to use try-catch
to capture and handle these errors:
import { arrayToTree, ArrayToTreeError } from "@okutils/array-to-tree";
try {
const tree = arrayToTree(data);
console.log(tree);
} catch (error) {
if (error instanceof ArrayToTreeError) {
console.error("Conversion failed:", error.message);
// Handle specific conversion errors
} else {
console.error("Unknown error:", error);
}
}
MIT © Luke Na