开发了一个树结构数组、扁平化数组相互转换的工具

Github:github.com/huyikai/tre...

Tree-Conver 是一个能将扁平节点数组与树形数组相互转换的工具。

对外导出 arrayToTreetreeToArray 两个方法。两个方法的基础情况下时间和空间复杂度均为 O(n),测试10w内数据的表现良好

treeToArray 方法增加了参数 addFieldsignoreFields,可以灵活的增减处理后数据中的字段。

安装使用

shell 复制代码
npm i tree-conver
javascript 复制代码
import { treeToArray, arrayToTree } from 'tree-conver';

数组转树

将一个扁平的节点数组转换成树形结构。

参数

它接受两个参数:

  • Array: 扁平的节点数组

  • Options: 一个可选的参数对象,用于配置转换方法的具体行为

    参数 描述 类型 默认值
    childrenKey 自定义节点 children 字段名称 string 'children'
    idKey 自定义节点 ID 字段名称 string 'id'
    pidKey 自定义节点父 ID 字段名称 string 'pid'

示例

javascript 复制代码
const flatArray = [
  { uid: '1', name: 'node1', pid: null },
  { uid: '2', name: 'node2', pid: '1' },
  { uid: '3', name: 'node3', pid: '1' },
  { uid: '4', name: 'node4', pid: '2' },
  { uid: '5', name: 'node5', pid: '2' },
  { uid: '6', name: 'node6', pid: '3' }
];

const options = {
  idKey: 'id',
  pidKey: 'pid',
  childrenKey: 'children'
};

const treeArray = arrayToTree(flatArray, options);

实现代码

typescript 复制代码
interface Node {
  id: string;
  children?: Array<Node>;
  pid?: string;
}
interface Options {
  idKey?: string;
  pidKey?: string;
  childrenKey?: string;
}

export const arrayToTree = (
  array: Array<Node | undefined>,
  options: Options = {}
) => {
  if (!Array.isArray(array)) {
    throw new Error('The first argument must be an array.');
  }
  const { idKey = 'id', pidKey = 'pid', childrenKey = 'children' } = options;
  const map = array.reduce((acc: Record<string, Node>, node: any) => {
    acc[node[idKey]] = { ...node, [childrenKey]: [] };
    return acc;
  }, {});

  Object.values(map).forEach((node: any) => {
    const parentId = node[pidKey];
    if (parentId) {
      const parent: any = map[parentId];
      if (!parent[childrenKey]) {
        parent[childrenKey] = [];
      }
      parent[childrenKey].push(node);
    }
  });
  const tree = Object.values(map).filter((node: any) => !node[pidKey]);
  return tree;
};

树转数组

将树形结构的数据转换为扁平的数组。

参数

接受两个参数:

  • Tree: 树形结构数组

  • Options: 一个可选的参数对象,用于配置转换方法的具体行为

    属性 描述 类型 默认值
    addFields 需要添加的字段名称及其对应的属性值计算方法的列表 [{ fieldName: string;callback: (item) => any }] []
    childrenKey 子节点的键名 string 'children'
    ignoreFields 要忽略的字段名称列表 string[] []
    needParentId 是否添加节点信息的父节点 ID boolean true

示例

javascript 复制代码
const treeArray = [
  {
    id: '1',
    name: 'Node 1',
    list: [
      {
        id: '2',
        name: 'Node 2',
        list: [
          {
            id: '3',
            name: 'Node 3'
          }
        ]
      },
      {
        id: '4',
        name: 'Node 4'
      }
    ]
  }
];
const calculateDepth = (node) => {
  let depth = 0;
  let parent = node;
  while (parent) {
    depth++;
    parent =
      parent['parentId'] && treeArray.find((n) => n.id === parent['parentId']);
  }
  return depth;
};
const options = {
  childrenKey: 'list',
  ignoreFields: [],
  addFields: [
    {
      fieldName: 'hasChildren', // Add a new 'field' property with a boolean value
      callback: (node) => Boolean(node['children'])
    },
    {
      fieldName: 'depth', // Add a new 'depth' property with the depth of each node
      callback: calculateDepth
    }
  ],
  needParentId: true
};

const flatArray = treeToArray(treeArray, options);

console.log(flatArray);
[
    {
      "id": "1",
      "name": "Node 1",
      "parentId": "",
      "hasChildren": false,
      "depth": 1
    },
    {
      "id": "2",
      "name": "Node 2",
      "parentId": "1",
      "hasChildren": false,
      "depth": 1
    },
    {
      "id": "3",
      "name": "Node 3",
      "parentId": "2",
      "hasChildren": false,
      "depth": 1
    },
    {
      "id": "4",
      "name": "Node 4",
      "parentId": "1",
      "hasChildren": false,
      "depth": 1
    }
]

实现代码

typescript 复制代码
interface Node {
  [key: string]: any;
  children?: Array<Node>;
}

interface TreeToArrayOptions {
  childrenKey?: string;
  ignoreFields?: Array<string>;
  addFields?: Array<{ fieldName: string; callback: (item: Node) => any }>;
  needParentId?: boolean;
}

export const treeToArray = (
  tree: Array<Node>,
  options: TreeToArrayOptions = {}
): Array<Node> => {
  const {
    childrenKey = 'children',
    ignoreFields = []
    addFields = [],
    needParentId = true
  } = options;
  const nodes: Array<Node> = [];
  const stack: Array<{
    node: Node | null;
    children: Array<Node>;
    parentId: string | null;
  }> = [];
  stack.push({
    node: null,
    children: tree,
    parentId: null
  });
  while (stack.length) {
    const { node, children, parentId } = stack.pop()!;
    if (node) {
      const { [childrenKey]: subChildren, ...rest } = node;
      const newNode = { ...rest };
      if (needParentId) {
        newNode['parentId'] = parentId;
      }
      if (addFields.length) {
        for (let i = 0; i < addFields.length; i++) {
          newNode[addFields[i].fieldName] = addFields[i].callback(node);
        }
      }
      if (ignoreFields.length) {
        for (let i = 0; i < ignoreFields.length; i++) {
          delete newNode[ignoreFields[i]];
        }
      }
      nodes.push(newNode);
    }
    if (children) {
      for (let i = children.length - 1; i >= 0; i--) {
        stack.push({
          node: children[i],
          children: children[i][childrenKey] || [],
          parentId: node?.id || ''
        });
      }
    }
  }
  return nodes;
};
相关推荐
艾小码17 分钟前
为什么你的Vue组件总出bug?可能是少了这份测试指南
前端·vue.js·debug
IT_陈寒17 分钟前
Redis 性能提升30%的7个关键优化策略,90%开发者都忽略了第3点!
前端·人工智能·后端
辞忧*29 分钟前
基于element-Plus的el-tooltip封装公共虚拟引用组件
前端·vue.js
by__csdn29 分钟前
Electron入门:跨平台桌面开发指南
前端·javascript·vue.js·typescript·electron·html
执笔论英雄34 分钟前
【大模型训练】forward_backward_func返回多个micro batch 损失
开发语言·算法·batch
草莓熊Lotso2 小时前
《算法闯关指南:优选算法--模拟》--41.Z 字形变换,42.外观数列
开发语言·c++·算法
啊吧怪不啊吧2 小时前
算法王冠上的明珠——动态规划之斐波那契数列问题
大数据·算法·动态规划
Nan_Shu_6144 小时前
学习:ES6(2)
前端·学习·es6
命运之光7 小时前
【最新】ChromeDriver最新版本下载安装教程,ChromeDriver版本与Chrome不匹配问题
前端·chrome