You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将字典数组重建为层级树数组时出现深层重复问题求助

问题:反转层级数据结构时深层节点重复

我参考了Scott Sauer的思路,尝试反转带层级结构的topics数据并去重,但修改函数后发现最深层的字典出现重复,希望找出问题所在。

原代码

const initObject = [
  { 
    id: 5,
    topics: [{
      id: 7,
      topics: [
        {id:9, topics:[]}
      ]
    }],
    parent: {
     id: 10,
     parent: {
       id: 15,
       parent: null
      }
    }
  },
  {
    id: 7,
    topics: [
        {id:9, topics:[]}
    ],
    parent: {
     id: 5,
     parent: {
       id: 10,
       parent: {
         id: 15,
         parent: null
       }
      }
    }
  }
]

function reverseGraphAll(data) {
    const rootTopics = [];
    const map = new Map([[undefined, rootTopics]]);
    
    function upward({id, parent, topics}) {
        if (map.has(id)) return map.get(id);
        let topicsNew = topics ? topics : [];
        map.set(id, topicsNew);
        (parent ? upward(parent) : rootTopics).push({id, topicsNew});
        return topicsNew;
    }
    
    data.forEach(upward);
    return rootTopics;
}

const result = reverseGraphAll(initObject);
console.log("Total", result);

问题根源

  1. 未处理节点自带的topics子节点:原函数仅处理了每个数据项的parent链,完全忽略了节点自身的topics子节点,导致这些子节点未被纳入统一的去重管理。
  2. 直接复用原数据的数组引用:代码中直接将原数据的topics数组赋值给topicsNew,没有创建新的数组或节点对象。当同一节点通过不同路径被处理时,会重复添加同一引用的节点,最终表现为深层字典重复。
  3. 缺乏重复校验:没有检查节点是否已经被添加到目标数组中,导致同一节点被多次插入。

修复方案

修改后的代码会先预注册所有节点,确保每个节点只被初始化一次;同时构建反转关系时加入重复校验,避免节点重复添加:

const initObject = [
  { 
    id: 5,
    topics: [{
      id: 7,
      topics: [
        {id:9, topics:[]}
      ]
    }],
    parent: {
     id: 10,
     parent: {
       id: 15,
       parent: null
      }
    }
  },
  {
    id: 7,
    topics: [
        {id:9, topics:[]}
    ],
    parent: {
     id: 5,
     parent: {
       id: 10,
       parent: {
         id: 15,
         parent: null
       }
      }
    }
  }
]

function reverseGraphAll(data) {
    const rootTopics = [];
    const map = new Map(); // 存储每个id对应的反转后子节点数组
    
    // 预注册所有节点(包括topics子节点和parent链节点)
    function registerNode(node) {
        if (!node || map.has(node.id)) return;
        map.set(node.id, []);
        // 递归注册自身topics子节点
        node.topics?.forEach(registerNode);
        // 递归注册parent链节点
        registerNode(node.parent);
    }
    
    // 构建反转后的层级关系
    function buildReverse(node) {
        if (!node) return;
        
        // 处理parent反转:将当前节点添加到父节点的子数组中
        if (node.parent) {
            const parentChildren = map.get(node.parent.id);
            if (!parentChildren.some(item => item.id === node.id)) {
                parentChildren.push({ id: node.id, topics: map.get(node.id) });
            }
        } else {
            // 无父节点则加入根数组
            if (!rootTopics.some(item => item.id === node.id)) {
                rootTopics.push({ id: node.id, topics: map.get(node.id) });
            }
        }
        
        // 处理topics反转:将当前节点添加到topics子节点的子数组中
        node.topics?.forEach(childNode => {
            const childChildren = map.get(childNode.id);
            if (!childChildren.some(item => item.id === node.id)) {
                childChildren.push({ id: node.id, topics: map.get(node.id) });
            }
            buildReverse(childNode);
        });
        
        buildReverse(node.parent);
    }
    
    // 执行注册和构建流程
    data.forEach(registerNode);
    data.forEach(buildReverse);
    
    return rootTopics;
}

const result = reverseGraphAll(initObject);
console.log("Total", JSON.stringify(result, null, 2));

修复说明

  • 预注册机制:先遍历所有节点(包括嵌套的topics和parent链),为每个id创建唯一的子节点数组,确保节点全局唯一。
  • 重复校验:每次添加节点前检查目标数组中是否已存在该节点,彻底避免重复。
  • 独立引用:所有反转后的节点对象都是新建的,不再复用原数据的数组或对象,确保引用独立,避免意外修改。
  • 双向关系处理:同时处理parent→child和topics→parent的反转逻辑,完整覆盖原数据的层级关系。

内容的提问来源于stack exchange,提问作者JimminyCricket

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:45:36