前端实现对一维数组化的树状结构转化

文章介绍了如何通过一维数组来简化树状结构的处理,特别是在前后端数据交互和前端展示中。利用Array.reduce方法,可以将后端提供的包含id、parent和pos信息的一维数组转换为树形结构,并添加path和children属性。此外,该方法具有一定的通用性,允许通过配置参数适应不同的字段名和节点处理需求。
摘要由CSDN通过智能技术生成

背景

树状结构是一种十分常见的数据结构,比如,企业的部门树、文件系统的目录树等等,都是树的典型使用场景。虽然,这些很可能是完全不同领域的业务逻辑;但是,围绕树状结构的基本操作,可以说,都是差不多的。另一方面,由于树状结构相较而言,会存在更多的一些复杂性,比如深度不确定的嵌套关系,给用户界面展示、数据搜索、业务逻辑处理、以及前后端数据交互,增加了复杂度。

设计思路

引入一个一维数组,将树“拍扁”,即可大大简化树状结构的数据读取与操作。尤其,如果系统采用了关系型数据库来做持久化,其数据本身也是以类似于一维数组的方式保存在表中的。因此,可以将这种方式再扩展到前后端数据交互、以及前端数据读取与操作。

下面将分享,前端从后端接收到一维数组,如何转化成树的数据结构。

代码

后端接口提供的一维数组的元素定义如下:

type Item = {
  id: number;
  parent?: number;	// 父节点ID
  pos?: number;		// 父节点下的兄弟数组中的下标值
  // ...
}

利用Array.reduce方法实现一维数组转换成树状结构。生成的树状结构的元素将会增加两个成员属性:

1)path:节点的完整路径;

2)children:子节点数组。

另外,为了提高通用性,数组元素的定义中如果不是默认的id、parent、pos,可以通过config参数的fieldNames进行修改适配。还可以利用config的callback方法对每个节点进行额外的处理,比如,不同的UI组件库对显示字段的命名不一样(比如,有的叫title,有的叫label,有的叫name,等等),就可以在这里处理。
 

/** 转化成树之后的节点数据定义 */
export type TreeNodeType<T> = T & {
  path: number[];				// 节点路径
  children?: TreeNodeType<T>[];	// 子节点数组
};

interface ArrayToTreeConfig<T, V = T> {
  parent: number;
  fieldNames: { id?: string; parent?: string; pos?: string };
  callback: ((value: TreeNodeType<T>) => TreeNodeType<V>) | undefined;
}

/** 一维数组转化树 */
export function arrayToTree<T, V = T>(
  arr: T[],
  config?: Partial<ArrayToTreeConfig<T, V>>,
) {
  const {
    id = 'id',
    parent = 'parent',
    pos = 'pos',
  } = config?.fieldNames || {};
  return loopArray(arr, [], {
    parent: config?.parent || 0,
    fieldNames: { id, parent, pos },
    callback: config?.callback,
  });
}

function loopArray<T, V = T>(
  arr: T[],
  path: number[],
  config: Omit<ArrayToTreeConfig<T, V>, 'fieldNames'> & {
    fieldNames: { id: string; parent: string; pos: string };
  },
) {
  const {
    parent,
    fieldNames: { id: idField, parent: parentField, pos: posField },
    callback,
  } = config;
  return arr.reduce((prev, current, _) => {
    if (((current as never)[parentField] || 0) === parent) {
      const pos = (current as never)[posField] || 0;
      (current as TreeNodeType<V>)['path'] = [...path, pos];
      const children = loopArray(arr, (current as TreeNodeType<V>)['path'], {
        ...config,
        parent: (current as never)[idField],
      });
      if (children.length > 0) {
        (current as TreeNodeType<V>)['children'] = children;
      }
      prev[pos] = (
        callback ? callback(current as TreeNodeType<T>) : current
      ) as never;
      return prev;
    }
    return prev;
  }, []) as TreeNodeType<V>[];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值