当前位置: 代码网 > it编程>编程语言>Javascript > js,关于 tree 数据结构的递归算法工具。

js,关于 tree 数据结构的递归算法工具。

2024年07月28日 Javascript 我要评论
关于js树的操作,写的都是最简单易懂的基础代码,特殊场景或特殊字段,需要自己在进行特殊改造。

javascript tree

背景:前端日常业务开发的工作中,关于树状数据结构的增删改查肯定避免不了, 这里整理下日常通用的工具函数,方便下次快速改造。

类型、使用数据示例

//以下函数示例的类型合数据 参考这里定义。
type ilist = {
  id: number;
  pid: number | null;
  value: string;
};
type itree = {
  children?: itree[];
} & ilist;

let list: ilist[] = [
  { id: 1, pid: null, value: "item 1" },
  { id: 2, pid: 1, value: "item 1-1" },
  { id: 3, pid: 1, value: "item 1-2" },
  { id: 4, pid: 2, value: "item 1-1-1" },
  { id: 5, pid: null, value: "item 2" },
];

let tree: itree[] = [
  {
    id: 1,
    pid: null,
    value: "item 1",
    children: [
      {
        id: 2,
        pid: 1,
        value: "item 1-1",
        children: [{ id: 4, pid: 2, value: "item 1-1-1" }],
      },
      { id: 3, pid: 1, value: "item 1-2" },
    ],
  },
  { id: 5, pid: null, value: "item 2" },
];

线性列表转树结构

/**
 * 匹配等于父节点的层,增加children,递归匹配查询
 * @param olddata 线性列表数据源
 * @param id 唯一标识
 * @param newdata 转完返回的树结构
 * @returns
 */
const listtotree = (
  olddata: ilist[],
  id: number | null,
  newdata: itree[] = []
) => {
  olddata.foreach((item) => {
    if (item.pid === id) {
      newdata.push(item);
    }
  });
  newdata.foreach((i) => {
    i.children = [];
    listtotree(olddata, i.id, i.children);
    if (i.children.length === 0) {
      delete i.children;
    }
  });
  return newdata;
};

console.log(listtotree(list, null));

树结构转线性列表

/**
 * 递归,把每一个节点push到新数组
 * @param treedata tree数据源
 * @param result 线性列表
 * @returns
 */
const flattentree = (treedata: itree[], result: ilist[] = []) => {
  treedata.foreach((node) => {
    result.push(node);
    if (node.children && node.children?.length > 0) {
      flattentree(node.children, result);
      delete node.children;
    }
  });

  return result;
};

console.log(flattentree(tree));

树:删除一个节点

/**
 * 删除树结构对应条件的某一节点  (引用删除)
 * @param treedata 数据源
 * @param id 唯一标识
 * @returns
 */
const removetreeitem = (treedata: itree[], id: number) => {
  if (!treedata || !treedata.length) {
    return;
  }
  for (let i = 0; i < treedata.length; i++) {
    let node = treedata[i];
    if (node.id === id) {
      treedata.splice(i, 1);
      break;
    }
    if (node.children && node.children?.length > 0) {
      removetreeitem(node.children, id);
    }
  }
  return treedata;
};
console.log(json.stringify(removetreeitem(tree, 2)));

树:批量过滤

/**
 * 批量过滤出树结构满足对应字段条件的节点 (从最顶层过滤,有缺陷)
 * @param {array}  treedata 要查找的源数组
 * @param {functiuon} ruleval   匹配的条件函数
 */
const filtertree = (treedata: itree[], ruleval: (item: itree) => boolean) => {
  var newdata = treedata.filter(ruleval);
  newdata.foreach(
    (item) =>
      item.children && (item.children = filtertree(item.children, ruleval))
  );
  return newdata;
};

console.log(
  json.stringify(
    filtertree(tree, (item: itree) => item.value.indexof("2") > -1)
  )
);

树:查询一个节点

/**
 *根据要查找的key返回这一项 (递归查找,引用查找)
 * @param {array}  treedata 要查找的源数组
 * @param {string} id   唯一标识
 * @param {string} key   要查找的键名
 */
const findtreeitem = (
  treedata: itree[],
  id: number,
  key: keyof itree = "id"
) => {
  for (const item of treedata) {
    if (item[key] === id) return item;
    if (item.children && item.children.length > 0) {
      let forfindret: any = findtreeitem(item.children, id, key);
      if (forfindret) return forfindret;
    }
  }
};

console.log(json.stringify(findtreeitem(tree, 2)));

树:增加一个节点

/**
 * 添加一个节点, (没有考虑节点的关联关系)
 * @param {array}  treedata 数据源
 * @param {number} id  唯一标识
 * @param {itree} newobj  新数据项
 */

const addtreeitem = (treedata: itree[], id: number | null, newobj: itree) => {
  if (!treedata || !treedata.length) {
    return;
  }
  if (id === null) {
    treedata.push(newobj);
    return treedata;
  }
  for (let i = 0; i < treedata.length; i++) {
    let node = treedata[i];
    if (node.id === id) {
      node.children ? node.children.push(newobj) : (node.children = [newobj]);
      break;
    }
    if (node.children && node.children?.length > 0) {
      addtreeitem(node.children, id, newobj);
    }
  }
  return treedata;
};
console.log(
  json.stringify(addtreeitem(tree, 5, { id: 6, pid: 5, value: "item 5-1" }))
);
console.log(
  json.stringify(addtreeitem(tree, 6, { id: 7, pid: 6, value: "item 6-1" }))
);
console.log(
  json.stringify(addtreeitem(tree, null, { id: 8, pid: null, value: "item 3" }))
);

树:批量修改

/**
 * 树:批量修改, (节点映射新字段)
 * @param  treedata 数据源
 * @param  maprule  自定义要映射的规则
 */

const maptree = <t>(
  treedata: itree[],
  maprule: (item: itree) => t
): (t & itree)[] => {
  return treedata.map((item) => ({
    ...item,
    ...maprule(item),
    children:
      item.children && item.children.length
        ? maptree(item.children, maprule)
        : null,
  }));
};

type inewfield = {
  newval: string;
  newfield: string;
};
console.log(
  json.stringify(
    maptree<inewfield>(tree, (item) => ({
      newval: "追加字段",
      newfield: item.value.indexof("1") > -1 ? "特殊标识" : "普通标识",
    }))
  )
);

树:查找树的最大层级

/**
 * 查找树的最大层级
 * @param  treedata 数据源
 * @returns {number}
 */
const findtreedepth = (treedata: itree[] | undefined): number => {
  if (!treedata || treedata.length === 0) {
    return 0;
  }
  let maxdepth = 0;
  for (let node of treedata) {
    const depth = findtreedepth(node.children);
    maxdepth = math.max(maxdepth, depth);
  }
  // 返回子节点中最大深度加上当前节点的深度
  return maxdepth + 1;
};
console.log(json.stringify(findtreedepth(tree)));

总结:上述代码中

  • 方便同学理解,所有的方法都是围绕上面定义的【数据示例】进行代码编写。
  • 关于js 树的操作,写的都是最简单易懂的基础代码,特殊场景或特殊字段,需要自己在进行特殊改造。

有疑问的同学可以私信我、对帮助到同学欢迎大家收藏评论。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com