重复数据删除时反转数据结构

Invert data structure while deduplicating

提问人:JimminyCricket 提问时间:12/15/2022 最后编辑:JimminyCricket 更新时间:12/16/2022 访问量:56

问:

我有一个复杂的任务,我正在努力寻找解决方案。我有以下数据结构

data = [
    {
        "id": 1,
        "attributes": {
            "name": "Mother of All Topics",
            "info": "Notice",
            "parentTopic": {
                "data": null
            }
        }
    },
    {
        "id": 2,
        "attributes": {
            "name": "Important Topic",
            "info": null,
            "parentTopic": {
                "data": {
                    "id": 1,
                    "attributes": {
                        "name": "Mother of All Topics",
                        "info": "Notice",
                        "parentTopic": {
                            "data": null
                        }
                    }
                }
            }
        }
    },
    {
        "id": 3,
        "attributes": {
            "name": "Critical Topic",
            "info": null,
            "parentTopic": {
                "data": {
                    "id": 2,
                    "attributes": {
                        "name": "Important Topic",
                        "info": null,
                        "parentTopic": {
                            "data": {
                                "id": 1,
                                "attributes": {
                                    "name": "Mother of All Topics",
                                    "info": "Notice",
                                    "parentTopic": {
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        "id": 5,
        "attributes": {
            "name": "Another Important Topic",
            "info": null,
            "parentTopic": {
                "data": {
                    "id": 1,
                    "attributes": {
                        "name": "Mother of All Topics",
                        "info": "Notice",
                        "parentTopic": {
                            "data": null
                        }
                    }
                }
            }
        }
    },
    {
        "id": 6,
        "attributes": {
            "name": "Demo Demo Topic",
            "info": null,
            "parentTopic": {
                "data": {
                    "id": 5,
                    "attributes": {
                        "name": "Another Important Topic",
                        "info": null,
                        "parentTopic": {
                            "data": {
                                "id": 1,
                                "attributes": {
                                    "name": "Mother of All Topics",
                                    "info": "Notice",
                                    "parentTopic": {
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        "id": 7,
        "attributes": {
            "name": "Some Topic",
            "info": null,
            "parentTopic": {
                "data": {
                    "id": 5,
                    "attributes": {
                        "name": "Another Important Topic",
                        "info": null,
                        "parentTopic": {
                            "data": {
                                "id": 1,
                                "attributes": {
                                    "name": "Mother of All Topics",
                                    "info": "Notice",
                                    "parentTopic": {
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        "id": 9,
        "attributes": {
            "name": "Nice Topic",
            "info": "Critical note",
            "parentTopic": {
                "data": {
                    "id": 1,
                    "attributes": {
                        "name": "Mother of All Topics",
                        "info": "Notice",
                        "parentTopic": {
                            "data": null
                        }
                    }
                }
            }
        }
    },
    {
        "id": 10,
        "attributes": {
            "name": "Super Critical Topic",
            "info": "Critical note",
            "parentTopic": {
                "data": {
                    "id": 9,
                    "attributes": {
                        "name": "Nice Topic",
                        "info": "Critical note",
                        "parentTopic": {
                            "data": {
                                "id": 1,
                                "attributes": {
                                    "name": "Mother of All Topics",
                                    "info": "Notice",
                                    "parentTopic": {
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        "id": 11,
        "attributes": {
            "name": "Just Another Example Topic",
            "info": null,
            "parentTopic": {
                "data": {
                    "id": 10,
                    "attributes": {
                        "name": "Super Critical Topic",
                        "info": "Critical note",
                        "parentTopic": {
                            "data": {
                                "id": 9,
                                "attributes": {
                                    "name": "Nice Topic",
                                    "info": "Critical note",
                                    "parentTopic": {
                                        "data": {
                                            "id": 1,
                                            "attributes": {
                                                "name": "Mother of All Topics",
                                                "info": "Notice",
                                                "parentTopic": {
                                                    "data": null
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        "id": 22,
        "attributes": {
            "name": "Nested Important Topic",
            "info": "",
            "parentTopic": {
                "data": null
            }
        }
    },
    {
        "id": 23,
        "attributes": {
            "name": "Nice Topic",
            "info": "",
            "parentTopic": {
                "data": {
                    "id": 22,
                    "attributes": {
                        "name": "Nested Important Topic",
                        "info": "",
                        "parentTopic": {
                            "data": null
                        }
                    }
                }
            }
        }
    },
    {
        "id": 24,
        "attributes": {
            "name": "Critical Top Level",
            "info": "",
            "parentTopic": {
                "data": null
            }
        }
    },
    {
        "id": 25,
        "attributes": {
            "name": "Nice Topic",
            "info": "",
            "parentTopic": {
                "data": {
                    "id": 24,
                    "attributes": {
                        "name": "Critical Top Level",
                        "info": "",
                        "parentTopic": {
                            "data": null
                        }
                    }
                }
            }
        }
    },
    {
        "id": 26,
        "attributes": {
            "name": "Stakeholder Management",
            "info": "Noice",
            "parentTopic": {
                "data": null
            }
        }
    },
    {
        "id": 27,
        "attributes": {
            "name": "Conflict Resolution",
            "info": "Watch out",
            "parentTopic": {
                "data": null
            }
        }
    },
    {
        "id": 28,
        "attributes": {
            "name": "International Resolution",
            "info": "Watch out",
            "parentTopic": {
                "data": null
            }
        }
    },
    {
        "id": 29,
        "attributes": {
            "name": "Gadget Management",
            "info": "",
            "parentTopic": {
                "data": {
                    "id": 25,
                    "attributes": {
                        "name": "Nice Topic",
                        "info": "",
                        "parentTopic": {
                            "data": {
                                "id": 24,
                                "attributes": {
                                    "name": "Critical Top Level",
                                    "info": "",
                                    "parentTopic": {
                                        "data": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
]

我尝试使用以下函数通过将其应用于字典数组来反转它,虽然由于某种原因它是一个递归函数,但它仅适用于一个嵌套级别。

// construct prefix
const getPrefix = (prefix, i) =>
  prefix ? `${prefix}.${i + 1}` : `${i + 1}`;

// invert hierarchy
const invertHierarchy = (arr, parentTopic, prefix) =>
  arr
    .filter((e) => e.parentTopic.data?.id === parentTopic)
    .map((e, i) => ({
      ...e,
      index: getPrefix(prefix, i),
      topics: invertHierarchy(arr, e.id, getPrefix(prefix, i)),
    }));

我的目标输出结果如下所示

[
    {
        "id": 1,
        "name": "Mother of All Topics",
        "topics": [
            {
                "id": 2,
                "name": "Important Topic",
                "info": null,
                "topics": [
                    {
                        "id": 3,
                        "name": "Critical Topic",
                        "topics": [

                        ]
                    }
                ]
            }
        ]  
    },
    {
        "id": 24,
        "name": "Critical Top Level",
        "topics": [
            {
                "id": 25,
                "name": "Nice Topic",
                "info": null,
                "topics": [
                    {
                        "id": 29,
                        "name": "Gadget Management",
                        "topics": null
                    }
                ]
            }
        ]  
    },
    ...
]

本质上,我的最终目标是最终得到倒置和重复数据删除的数据结构,其中每个父主题都位于顶部,所有子主题都正确嵌套在主题数组中。

如果有人能帮助解决这一挑战,我将不胜感激

!更新

[
  {
    id: 22,
    attributes: {
      name: 'Nested Important Topic',
      info: '',
      createdAt: '2022-12-06T13:37:51.626Z',
      updatedAt: '2022-12-06T13:37:51.691Z',
      parentTopic: [Object],
      questions: [Object]
    }
  },
  {
    id: 23,
    attributes: {
      name: 'Nice Topic',
      info: '',
      parentTopic: {
        data: {
          id: 36,
          name: "Critical High Level Topic",
          info: ''
        }
      },
      questions: [Object]
    }
  },
  {
    id: 25,
    attributes: {
      name: 'Nice Topic',
      info: '',
      parentTopic: [Object],
      questions: [Object]
    }
  }
]

进一步更新!

输入

[
    {
        "id": 25,
        "attributes": {
            "name": "Nice Topic",
            "info": "",
            "parentTopic": {
                "data": {
                    "id": 24,
                    "attributes": {
                        "name": "Critical Top Level",
                        "info": "",
                        "parentTopic": {
                            "data": null
                        }
                    }
                }
            }
        }
    },
    {
        "id": 23,
        "attributes": {
            "name": "Nice Topic",
            "info": "",
            "parentTopic": {
                "data": {
                    "id": 22,
                    "attributes": {
                        "name": "Nested Important Topic",
                        "info": "",
                        "parentTopic": {
                            "data": null
                        },
                    }
                }
            },
        }
    },
    {
        "id": 22,
        "attributes": {
            "name": "Nested Important Topic",
            "info": "",
            "parentTopic": {
                "data": null
            }
        }
    }
],

输出!

[
    {
        "id": 24,
        "name": "Critical Top Level",
        "info": "",
        "topics": [
            {
                "id": 25,
                "name": "Nice Topic",
                "info": "",
                "topics": []
            }
        ]
    },
    {
        "id": 22,
        "name": "Nested Important Topic",
        "info": "",
        "topics": [
            {
                "id": 23,
                "name": "Nice Topic",
                "info": "",
                "topics": []
            }
        ]
    },
    {
        "id": 22,
        "name": "Nested Important Topic",
        "info": "",
        "topics": []
    }
],

JavaScript 递归 嵌套

评论

0赞 trincot 12/16/2022
可以编辑上次编辑吗?它不应该包含(通常是控制台输出),因为这不是有效的 JavaScript 语法。此外,读者也不清楚该更新是什么:您能否明确指定这是输入指定您期望的相应输出?[Object]

答:

1赞 trincot 12/15/2022 #1

您的输入结构看起来像是具有重复的对象,并且可能最初具有对同一对象的重复引用(因此不是类似 JSON 的)。

我假设,如果在输入的一个区域中我们看到 24 是 25 的父节点,那么当我们在其他地方遇到节点 25 时也是如此。换言之,一个项目只有一个父项。

您可以反转数据结构,方法是首先在按对象的 id 键控的 Map 中收集对象,然后将每个 id 与具有 和 属性的新对象相关联。前两个可以立即设置,而属性将以空数组开始。idnametopicstopics

对于每个对象,您可以递归地遍历祖先的路径,然后在回溯时将项目存储在父级的数组中。topics

下面是一个实现:

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

// Your example data:
const data = [{"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}},{"id": 2,"attributes": {"name": "Important Topic","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}},{"id": 3,"attributes": {"name": "Critical Topic","parentTopic": {"data": {"id": 2,"attributes": {"name": "Important Topic","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}}}}},{"id": 5,"attributes": {"name": "Another Important Topic","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}},{"id": 6,"attributes": {"name": "Demo Demo Topic","parentTopic": {"data": {"id": 5,"attributes": {"name": "Another Important Topic","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}}}}},{"id": 7,"attributes": {"name": "Some Topic","parentTopic": {"data": {"id": 5,"attributes": {"name": "Another Important Topic","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}}}}},{"id": 9,"attributes": {"name": "Nice Topic","info": "Critical note","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}},{"id": 10,"attributes": {"name": "Super Critical Topic","info": "Critical note","parentTopic": {"data": {"id": 9,"attributes": {"name": "Nice Topic","info": "Critical note","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}}}}},{"id": 11,"attributes": {"name": "Just Another Example Topic","parentTopic": {"data": {"id": 10,"attributes": {"name": "Super Critical Topic","info": "Critical note","parentTopic": {"data": {"id": 9,"attributes": {"name": "Nice Topic","info": "Critical note","parentTopic": {"data": {"id": 1,"attributes": {"name": "Mother of All Topics","parentTopic": {"data": null}}}}}}}}}}}},{"id": 22,"attributes": {"name": "Nested Important Topic","parentTopic": {"data": null}}},{"id": 23,"attributes": {"name": "Nice Topic","parentTopic": {"data": {"id": 22,"attributes": {"name": "Nested Important Topic","parentTopic": {"data": null}}}}}},{"id": 24,"attributes": {"name": "Critical Top Level","parentTopic": {"data": null}}},{"id": 25,"attributes": {"name": "Nice Topic","parentTopic": {"data": {"id": 24,"attributes": {"name": "Critical Top Level","parentTopic": {"data": null}}}}}},{"id": 26,"attributes": {"name": "Stakeholder Management","info": "Noice","parentTopic": {"data": null}}},{"id": 27,"attributes": {"name": "Conflict Resolution","parentTopic": {"data": null}}},{"id": 28,"attributes": {"name": "International Resolution","parentTopic": {"data": null}}},{"id": 29,"attributes": {"name": "Gadget Management","parentTopic": {"data": {"id": 25,"attributes": {"name": "Nice Topic","parentTopic": {"data": {"id": 24,"attributes": {"name": "Critical Top Level","parentTopic": {"data": null}}}}}}}}}];

const result = reverseGraph(data);
console.log(result);

评论

0赞 JimminyCricket 12/16/2022
感谢您的解决方案@trincot,但不幸的是,我认为它假设所有嵌套项目至少应该存在于顶层,因为当我通过一个字典时,假设第一个级别中从未出现过第三个嵌套级别,它会抛出一个错误,说无法读取未定义的属性。我将编辑我的问题并添加数据结构示例
0赞 trincot 12/16/2022
“我认为它假设”:我把它列为假设,是的。感谢您更新您的问题,因为在原文中就是这种情况。
0赞 trincot 12/16/2022
我更新了我的答案,不再做出这个假设。
0赞 JimminyCricket 12/16/2022
非常感谢@trincot,这确实有助于消除这种假设,但我认为关于主题值的顺序还有一个假设。我用数据字典在问题中进行了另一次更新。基本上,它会在浏览数据后保留重复项。你认为你也可以帮忙解决这个问题吗?
0赞 trincot 12/16/2022
哦,所以有时一个节点在顶层重复,有时不是......我已经相应地更新了我的答案。