npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@suzilong/tree

v1.1.0

Published

功能全面的树操作库,提供树的遍历、搜索、修改等功能,适用于浏览器和node.js等环境。

Readme

tree

一个现代、轻量的 TS 树操作库,涵盖遍历(深度优先前序/后序、广度优先)、查找、转换、查询与修改等核心需求。纯函数设计确保数据不可变,支持自定义子节点字段,零依赖且类型完备,浏览器与 Node.js 均可使用。

English Version (中文版本)

特性

  • 🚀 全面:涵盖树操作的常见场景
  • 🔒 不可变:函数式编程,无副作用
  • 🎯 灵活:支持自定义子节点字段名、遍历策略(DFS/BFS)和遍历顺序(前序,后序)
  • 📦 轻量:无依赖,体积小巧(gzip后仅~3K),支持Tree-shaking
  • 🌲 支持森林:可以处理多根节点的树(森林)
  • 🛡️ 健壮:纯 TypeScript 编写,类型安全,测试用例覆盖全面

安装

npm i @suzilong/tree
# 或
yarn add @suzilong/tree
# 或
pnpm i @suzilong/tree

使用示例

Es Module

import { forEach } from '@suzilong/tree'

forEach(tree, (node) => {
    console.log(node)
})

Node.js

const { forEach } = require('@suzilong/tree')

forEach(tree, (node) => {
    console.log(node)
})

直接在浏览器中使用

<script src="https://unpkg.com/@suzilong/tree"></script>

<script>
    const { forEach } = window.Tree

    forEach(tree, (node) => {
        console.log(node)
    })
</script>

API 文档

目录


1. 遍历 (traverse)

forEach

功能:对树中的每个节点执行一次给定的函数(无返回值,不中断)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => void - 对每个节点执行的函数
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'(深度优先)
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'(前序遍历)

示例

import { forEach } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1', children: [{ id: '1-1-1' }] }],
}

forEach(tree, (node, context) => {
    console.log(`节点:${node.id}, 深度:${context.depth}`)
})
// 输出:节点:1, 深度:0
// 节点:1-1, 深度:1
// 节点:1-1-1, 深度:2

depthFirst

功能:深度优先遍历树结构

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => boolean | void - 对每个节点执行的函数,返回 false 可中断遍历
  • options: DepthFirstOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • order: 'pre' | 'post' - 遍历顺序,默认为 'pre'(前序遍历)

示例

import { depthFirst } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1', children: [{ id: '1-1-1' }] }],
}

// 前序遍历(默认)
depthFirst(tree, (node) => {
    console.log(node.id)
})
// 输出:1, 1-1, 1-1-1

breadthFirst

功能:广度优先遍历树结构

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => boolean | void - 对每个节点执行的函数,返回 false 可中断遍历
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

示例

import { breadthFirst } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1' }, { id: '1-2' }],
}

breadthFirst(tree, (node) => {
    console.log(node.id)
})
// 输出:1, 1-1, 1-2

2. 查找 (find)

find

功能:在树结构中查找满足条件的节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode) => boolean - 条件函数,返回 true 表示找到目标节点
  • options: FindOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode | null - 满足条件的节点,如果未找到则返回 null

示例

import { find } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1' }, { id: '1-2' }],
}

const foundNode = find(tree, (node) => node.id === '1-2')
console.log(foundNode) // 输出:{ id: '1-2' }

findAll

功能:在树结构中查找所有满足条件的节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode) => boolean - 条件函数,返回 true 表示找到目标节点
  • options: FindOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode[] - 满足条件的节点数组,如果未找到则返回空数组

示例

import { findAll } from '@suzilong/tree'

const tree = {
    id: '1',
    type: 'parent',
    children: [
        { id: '1-1', type: 'child' },
        { id: '1-2', type: 'child' },
    ],
}

const childNodes = findAll(tree, (node) => node.type === 'child')
console.log(childNodes.map((node) => node.id)) // 输出:['1-1', '1-2']

findPath

功能:在树结构中查找满足条件的节点,并返回从根节点到该节点的路径

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode) => boolean - 条件函数,返回 true 表示找到目标节点
  • options: FindOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode[] - 根节点到该节点的路径,如果未找到则返回空数组

示例

import { findPath } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1', children: [{ id: '1-1-1' }] }],
}

const path = findPath(tree, (node) => node.id === '1-1-1')
console.log(path.map((node) => node.id)) // 输出:['1', '1-1', '1-1-1']

3. 修改 (modify)

appendChild

功能:向指定父节点追加一个子节点(作为最后一个子节点),只操作第一个匹配的父节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位父节点(只使用第一个匹配的节点)
  • newNode: TreeNode - 要追加的新节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到父节点,则返回原树)

示例

import { appendChild } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }] }

const newTree = appendChild(tree, (node) => node.id === '1', { id: '1-2' })
console.log(newTree.children.map((node) => node.id)) // 输出:['1-1', '1-2']

prependChild

功能:向指定父节点 prepend 一个子节点(作为第一个子节点),只操作第一个匹配的父节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位父节点(只使用第一个匹配的节点)
  • newNode: TreeNode - 要 prepend 的新节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到父节点,则返回原树)

示例

import { prependChild } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }] }

const newTree = prependChild(tree, (node) => node.id === '1', { id: '1-0' })
console.log(newTree.children.map((node) => node.id)) // 输出:['1-0', '1-1']

insertBefore

功能:在指定节点前插入一个新节点,只操作第一个匹配的目标节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点(只使用第一个匹配的节点)
  • newNode: TreeNode - 要插入的新节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到目标节点,则返回原树)

示例

import { insertBefore } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const newTree = insertBefore(tree, (node) => node.id === '1-2', { id: '1-1.5' })
console.log(newTree.children.map((node) => node.id)) // 输出:['1-1', '1-1.5', '1-2']

insertAfter

功能:在指定节点后插入一个新节点,只操作第一个匹配的目标节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点(只使用第一个匹配的节点)
  • newNode: TreeNode - 要插入的新节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到目标节点,则返回原树)

示例

import { insertAfter } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const newTree = insertAfter(tree, (node) => node.id === '1-1', { id: '1-1.5' })
console.log(newTree.children.map((node) => node.id)) // 输出:['1-1', '1-1.5', '1-2']

remove

功能:移除树中所有满足条件的节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位要移除的节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:null | TreeNode | TreeNode[] - 新树(如果未找到要移除的节点,则返回原树)

示例

import { remove } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const newTree = remove(tree, (node) => node.id === '1-1')
console.log(newTree.children.map((node) => node.id)) // 输出:['1-2']

replace

功能:替换树中所有满足条件的节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位要替换的节点
  • newNode: TreeNode - 新节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到要替换的节点,则返回原树)

示例

import { replace } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const newTree = replace(tree, (node) => node.id === '1-1', { id: '1-1-new' })
console.log(newTree.children.map((node) => node.id)) // 输出:['1-1-new', '1-2']

move

功能:将一个节点移动到另一个节点的子节点列表中(作为最后一个子节点),只操作第一个匹配的源节点和目标节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • sourcePredicate: (node: TreeNode) => boolean - 断言函数,用于定位要移动的节点(只使用第一个匹配的节点)
  • targetPredicate: (node: TreeNode) => boolean - 断言函数,用于定位目标父节点(只使用第一个匹配的节点)
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到源节点或目标节点,则返回原树)

示例

import { move } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1' }, { id: '1-2', children: [] }],
}

const newTree = move(
    tree,
    (node) => node.id === '1-1',
    (node) => node.id === '1-2'
)
console.log(newTree.children[0].children.map((node) => node.id)) // 输出:['1-1']

swap

功能:交换树中两个节点的位置,只操作第一个匹配的节点

参数

  • tree: TreeNode | TreeNode[] - 原树或森林
  • predicate1: (node: TreeNode) => boolean - 断言函数,用于定位第一个节点
  • predicate2: (node: TreeNode) => boolean - 断言函数,用于定位第二个节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 新树(如果未找到节点或无法交换,则返回原树)

示例

import { swap } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [
        { id: 'A', value: 1 },
        { id: 'B', value: 2 },
    ],
}

const newTree = swap(
    tree,
    (node) => node.id === 'A',
    (node) => node.id === 'B'
)
console.log(newTree.children.map((node) => node.value)) // 输出:[2, 1]

4. 转换 (transform)

arrayToTree

功能:将扁平数组转换为树结构

参数

  • array: any[] - 扁平数组
  • options: ArrayToTreeOptions - 配置选项
    • idKey: string - 节点唯一标识字段名,默认为 'id'
    • parentIdKey: string - 父节点标识字段名,默认为 'parentId'
    • childrenKey: string - 子节点数组字段名,默认为 'children'
    • rootParentValue: null | undefined | string | number - 根节点的父标识值,默认为 null 或 undefined

返回值:TreeNode[] - 转换后的树结构(森林)

示例

import { arrayToTree } from '@suzilong/tree'

const array = [
    { id: '1', name: '节点1', parentId: null },
    { id: '2', name: '节点2', parentId: '1' },
    { id: '3', name: '节点3', parentId: '1' },
    { id: '4', name: '节点4', parentId: '2' },
]

const tree = arrayToTree(array)
console.log(tree[0].id) // 输出: '1'
console.log(tree[0].children.length) // 输出: 2
console.log(tree[0].children[0].children[0].id) // 输出: '4'

treeToArray

功能:将树结构转换为扁平数组

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: TreeToArrayOptions - 配置选项
    • idKey: string - 节点唯一标识字段名,默认为 'id'
    • parentIdKey: string - 输出中父节点标识字段名,默认为 'parentId'
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • rootParentValue: null | undefined | string | number - 根节点的父标识值,默认为 null
    • keepChildren: boolean - 是否在输出中保留子节点数组,默认为 false(移除 children)
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 遍历顺序,仅对 dfs 有效,默认为 'pre'

返回值:any[] - 转换后的扁平数组

示例

import { treeToArray } from '@suzilong/tree'

const tree = [{ id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }]

const array = treeToArray(tree)
console.log(array.map((item) => item.id)) // 输出:['1', '1-1', '1-2']

map

功能:对树中的每个节点执行映射函数,返回新树

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => TreeNode - 映射函数,返回新节点
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode | TreeNode[] - 映射后的新树

示例

import { map } from '@suzilong/tree'

const tree = { id: '1', value: 10, children: [{ id: '1-1', value: 20 }] }

const newTree = map(tree, (node) => ({
    ...node,
    value: node.value * 2,
}))
console.log(newTree.value) // 输出:20

filter

功能:过滤树中的节点,返回新树

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => boolean - 过滤函数,返回 true 表示保留节点
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode | TreeNode[] - 过滤后的新树

示例

import { filter } from '@suzilong/tree'

const tree = {
    id: '1',
    type: 'parent',
    children: [
        { id: '1-1', type: 'child' },
        { id: '1-2', type: 'parent' },
    ],
}

const filteredTree = filter(tree, (node) => node.type === 'parent')
console.log(filteredTree.children.length) // 输出:1

reduce

功能:对树中的节点执行归约函数,返回累加结果

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (accumulator: any, node: TreeNode, context: Context) => any - 归约函数
  • initialValue: any - 初始累加值
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:any - 归约结果

示例

import { reduce } from '@suzilong/tree'

const tree = { id: '1', value: 10, children: [{ id: '1-1', value: 20 }] }

const sum = reduce(tree, (acc, node) => acc + node.value, 0)
console.log(sum) // 输出:30

flat

功能:将树结构扁平化为节点数组

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode[] - 扁平后的节点数组

示例

import { flat } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const nodes = flat(tree)
console.log(nodes.map((node) => node.id)) // 输出:['1', '1-1', '1-2']

5. 查询 (query)

getCount

功能:获取树中节点的数量

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:number - 节点数量

示例

import { getCount } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const count = getCount(tree)
console.log(count) // 输出:3

getLeafCount

功能:获取树中叶子节点的数量

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:number - 叶子节点数量

示例

import { getLeafCount } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const leafCount = getLeafCount(tree)
console.log(leafCount) // 输出:2

getDepth

功能:获取树的深度

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:number - 树的深度

示例

import { getDepth } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const depth = getDepth(tree)
console.log(depth) // 输出:2

getAncestors

功能:获取指定节点的所有祖先节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode[] - 祖先节点数组(从根节点到父节点)

示例

import { getAncestors } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const ancestors = getAncestors(tree, (node) => node.id === '1-1')
console.log(ancestors.map((node) => node.id)) // 输出:['1']

getDescendants

功能:获取指定节点的所有后代节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:TreeNode[] - 后代节点数组

示例

import { getDescendants } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const descendants = getDescendants(tree, (node) => node.id === '1')
console.log(descendants.map((node) => node.id)) // 输出:['1-1', '1-2']

getSiblings

功能:获取指定节点的所有兄弟节点(不包括自身)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode[] - 兄弟节点数组

示例

import { getSiblings } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '1-1' }, { id: '1-2' }, { id: '1-3' }],
}

const siblings = getSiblings(tree, (node) => node.id === '1-2')
console.log(siblings.map((node) => node.id)) // 输出:['1-1', '1-3']

getParent

功能:获取指定节点的父节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | null - 父节点,如果目标不存在或是根节点则返回 null

示例

import { getParent } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const parent = getParent(tree, (node) => node.id === '1-1')
console.log(parent?.id) // 输出:'1'

getChildren

功能:获取指定节点的所有子节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 断言函数,用于定位目标节点
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode[] - 子节点数组,如果目标不存在或没有子节点则返回空数组

示例

import { getChildren } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const children = getChildren(tree, (node) => node.id === '1')
console.log(children.map((node) => node.id)) // 输出:['1-1', '1-2']

6. 其他 (orther)

clone

功能:深拷贝树结构

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:TreeNode | TreeNode[] - 拷贝后的新树

示例

import { clone } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }] }

const clonedTree = clone(tree)
console.log(clonedTree !== tree && clonedTree.children !== tree.children) // 输出:true

every

功能:检查树中的所有节点是否都满足条件

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => boolean - 条件函数
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:boolean - 所有节点都满足条件返回 true,否则返回 false

示例

import { every } from '@suzilong/tree'

const tree = { id: '1', type: 'node', children: [{ id: '1-1', type: 'node' }] }

const allNodes = every(tree, (node) => node.type === 'node')
console.log(allNodes) // 输出:true

some

功能:检查树中是否至少有一个节点满足条件

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • callback: (node: TreeNode, context: Context) => boolean - 条件函数
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'
    • strategy: 'dfs' | 'bfs' - 遍历策略,默认为 'dfs'
    • order: 'pre' | 'post' - 仅在深度优先遍历时有效,默认为 'pre'

返回值:boolean - 至少有一个节点满足条件返回 true,否则返回 false

示例

import { some } from '@suzilong/tree'

const tree = {
    id: '1',
    type: 'parent',
    children: [{ id: '1-1', type: 'child' }],
}

const hasChild = some(tree, (node) => node.type === 'child')
console.log(hasChild) // 输出:true

print

功能:在控制台打印tree,用于调试,打印结果类似Linux tree命令

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • options: Options - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:无

示例

import { print } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }] }

print(tree)
// 输出:
// └── 1
//     └── 1-1

7. 关系判断 (is)

isSibling

功能:判断两个节点是否为兄弟节点(即具有相同的父节点)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicateA: (node: TreeNode) => boolean - 定位第一个节点的断言函数
  • predicateB: (node: TreeNode) => boolean - 定位第二个节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是兄弟则返回 true,否则 false

示例

import { isSibling } from '@suzilong/tree'

const tree = {
    id: '1',
    children: [{ id: '2' }, { id: '3' }, { id: '4' }],
}

const areBrothers = isSibling(
    tree,
    (node) => node.id === 2,
    (node) => node.id === 3
)
console.log(areBrothers) // 输出:true

isAncestorOf

功能:判断 ancestor 节点是否是 descendant 节点的祖先(即 ancestor 在从根节点到 descendant 的路径上)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • ancestorPredicate: (node: TreeNode) => boolean - 定位祖先节点的断言函数
  • descendantPredicate: (node: TreeNode) => boolean - 定位后代节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是祖先关系则返回 true,否则 false

示例

import { isAncestorOf } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1', children: [{ id: '1-1-1' }] }] }

const isRootAncestor = isAncestorOf(
    tree,
    (node) => node.id === '1',
    (node) => node.id === '1-1-1'
)
console.log(isRootAncestor) // 输出:true

isDescendantOf

功能:判断 descendant 节点是否是 ancestor 节点的后代(即 descendant 在 ancestor 的子树中)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • descendantPredicate: (node: TreeNode) => boolean - 定位后代节点的断言函数
  • ancestorPredicate: (node: TreeNode) => boolean - 定位祖先节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是后代关系则返回 true,否则 false

示例

import { isDescendantOf } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1', children: [{ id: '1-1-1' }] }] }

const isLeafDescendant = isDescendantOf(
    tree,
    (node) => node.id === '1-1-1',
    (node) => node.id === '1'
)
console.log(isLeafDescendant) // 输出:true

isParentOf

功能:判断 parent 节点是否是 child 节点的直接父节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • parentPredicate: (node: TreeNode) => boolean - 定位父节点的断言函数
  • childPredicate: (node: TreeNode) => boolean - 定位子节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是直接父子关系则返回 true,否则 false

示例

import { isParentOf } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1', children: [{ id: '1-1-1' }] }] }

const isDirectParent = isParentOf(
    tree,
    (node) => node.id === '1',
    (node) => node.id === '1-1'
)
console.log(isDirectParent) // 输出:true

isChildOf

功能:判断 child 节点是否是 parent 节点的直接子节点

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • childPredicate: (node: TreeNode) => boolean - 定位子节点的断言函数
  • parentPredicate: (node: TreeNode) => boolean - 定位父节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是直接父子关系则返回 true,否则 false

示例

import { isChildOf } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1', children: [{ id: '1-1-1' }] }] }

const isDirectChild = isChildOf(
    tree,
    (node) => node.id === '1-1',
    (node) => node.id === '1'
)
console.log(isDirectChild) // 输出:true

isRoot

功能:判断节点是否为根节点(即没有父节点)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 定位节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是根节点则返回 true,否则 false

示例

import { isRoot } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }] }

const isRootNode = isRoot(tree, (node) => node.id === '1')
console.log(isRootNode) // 输出:true

isLeaf

功能:判断节点是否为叶子节点(即没有子节点)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicate: (node: TreeNode) => boolean - 定位节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 是叶子节点则返回 true,否则 false

示例

import { isLeaf } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }] }

const isLeafNode = isLeaf(tree, (node) => node.id === '1-1')
console.log(isLeafNode) // 输出:true

isSameDepth

功能:判断两个节点是否在同一深度(即同一层)

参数

  • tree: TreeNode | TreeNode[] - 树或森林
  • predicateA: (node: TreeNode) => boolean - 定位第一个节点的断言函数
  • predicateB: (node: TreeNode) => boolean - 定位第二个节点的断言函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 如果两个节点深度相同则返回 true,否则 false

示例

import { isSameDepth } from '@suzilong/tree'

const tree = { id: '1', children: [{ id: '1-1' }, { id: '1-2' }] }

const sameDepth = isSameDepth(
    tree,
    (node) => node.id === '1-1',
    (node) => node.id === '1-2'
)
console.log(sameDepth) // 输出:true

isEqual

功能:比较两棵树是否相等(通过自定义比较函数)

参数

  • tree1: TreeNode | TreeNode[] - 第一棵树或森林
  • tree2: TreeNode | TreeNode[] - 第二棵树或森林
  • compare: (node1: TreeNode, node2: TreeNode) => boolean - 节点比较函数
  • options: BaseOptions - 配置选项
    • childrenKey: string - 自定义子节点字段名,默认为 'children'

返回值:boolean - 如果两棵树结构相同且所有对应节点都满足比较函数则返回 true,否则 false

示例

import { isEqual } from '@suzilong/tree'

const tree1 = { id: '1', children: [{ id: '1-1', value: 10 }] }
const tree2 = { id: '1', children: [{ id: '1-1', value: 99 }] }

const equalById = isEqual(tree1, tree2, (n1, n2) => n1.id === n2.id)
console.log(equalById) // 输出:true

类型定义

TreeNode

type TreeNode<T = unknown, ChildKey extends string = 'children'> = {
    [key: string]: unknown // 允许任意其他属性
} & {
    [K in ChildKey]?: TreeNode<T, ChildKey>[] // 动态子节点字段
}

Context

interface Context {
    index: number // 当前节点在兄弟节点中的位置(从 0 开始)
    depth: number // 当前节点深度(根节点为 0)
    parent: TreeNode | null // 父节点,根节点为 null
    path: TreeNode[] // 从根到当前节点的路径
}

Options

interface Options extends BaseOptions {
    strategy?: 'dfs' | 'bfs' // 遍历策略,默认为 'dfs'(深度优先),可选 'bfs'(广度优先)
    order?: 'pre' | 'post' // 仅在深度优先遍历时有效,默认为 'pre'(前序遍历),可选 'post'(后序遍历)
}

BaseOptions

interface BaseOptions {
    childrenKey?: string // 自定义子节点字段名,默认为 'children'
}

ArrayToTreeOptions

interface ArrayToTreeOptions {
    /** 节点唯一标识字段名,默认为 'id' */
    idKey?: string
    /** 父节点标识字段名,默认为 'parentId' */
    parentIdKey?: string
    /** 子节点数组字段名,默认为 'children' */
    childrenKey?: string
    /** 根节点的父标识值,默认为 null 或 undefined */
    rootParentValue?: null | undefined | string | number
}

TreeToArrayOptions

interface TreeToArrayOptions {
    /** 节点唯一标识字段名,默认为 'id' */
    idKey?: string
    /** 输出中父节点标识字段名,默认为 'parentId' */
    parentIdKey?: string
    /** 自定义子节点字段名,默认为 'children' */
    childrenKey?: string
    /** 根节点的父标识值,默认为 null */
    rootParentValue?: null | undefined | string | number
    /** 是否在输出中保留子节点数组,默认为 false(移除 children) */
    keepChildren?: boolean
    /** 遍历策略,默认为 'dfs' */
    strategy?: 'dfs' | 'bfs'
    /** 遍历顺序,仅对 dfs 有效,默认为 'pre' */
    order?: 'pre' | 'post'
}