0

I have the following data structure

[
    {
        "id": 3,
        "name" "Important Topic 3",
        "questions": [array of questions],
        "topics: [array of topics],
        "parentTopic": {
                "id": 2,
                "name": "Parent Topic 1",
                "parentTopic": {
                    "id": 1,
                    "name": "Parent Topic 2",
                    "parentTopic: null
                 }
            }
     },
    {
        "id": 4,
        "name" "Important Topic 4",
        "questions": [array of questions],
        "topics: [array of topics],
        "parentTopic": {
                "id": 2,
                "name": "Parent Topic 1",
                "parentTopic": {
                    "id": 1,
                    "name": "Parent Topic 2",
                    "parentTopic: null
                 }
            }
     }
]

I want to end up with the following

[
    {
        "id": 1,
        "name": "Parent Topic 2",
        "topics": [
            {
                "id": 2,
                "name": "Parent Topic 1"
                "topics": [
                    {
                      "id": 3,
                      "name: "Important Topic 3",
                      "questions": [array of questions],
                      "topics: [array of topics]
                    },
                    {
                      "id": 4,
                      "name: "Important Topic 4",
                      "questions": [array of questions],
                      "topics: [array of topics]
                    }
                ]
            }
        ]
    }
]

I have some code which I got also as help from the awesome stackoverflow community, but it only works if I have nested dictionary objects, not lists of dictionaries in an array. Here is the stackoverflow reference - Reverse Nested Tree in Javascript

I would really appreciate if you could help solve this puzzle :)

1
  • Your input has some unclear references, like "array of topics". As in your output you have wishes about what topics should be, the question is then what happens to -- and what is the relevance of -- the original topics properties? Commented Nov 13, 2022 at 10:43

2 Answers 2

1

You could collect nodes -- as you visit them -- in a map, keyed by their id, so you can know when you meet a node that you already have placed in the target structure. So visit the source nodes in depth-first order (pre-order) and build the new hierarchy bottom-up, but stopping when a node is encountered that is already in the target structure. At that point put the last created target node inside its topics array.

Here is an implementation of that idea:

function invertHierarchy(leaves) {
    const root = { id: null, topics: [] };
    const map = { null: root }; 
    for (let { id, parentTopic, ...rest } of leaves) {
        let node = map[id] = { id, ...rest }; // leaf
        while (true) {
            ({id, parentTopic, ...rest} = parentTopic ?? root);
            if (id in map) break;
            map[id] = node = { id, ...rest, topics: [node] };
        }
        map[id].topics.push(node);
    }
    return root.topics;
}

// Example run:
const leaves = [{"id": 3,"name": "Important Topic 3","questions": [],"topics": ["a"],"parentTopic": {"id": 2,"name": "Parent Topic 1","parentTopic": {"id": 1,"name": "Parent Topic 2","parentTopic": null}}},{"id": 4,"name": "Important Topic 4","questions": [],"topics": ["b"],"parentTopic": {"id": 2,"name": "Parent Topic 1","parentTopic": {"id": 1,"name": "Parent Topic 2","parentTopic": null}}}];
const result = invertHierarchy(leaves);
console.log(result);

Sign up to request clarification or add additional context in comments.

Comments

0

This seems to achieve what you want :

// The json data
const jsonData = `[
  {
      "id": 3,
      "name": "Important Topic 3",
      "questions": [],
      "topics": [],
      "parentTopic": {
              "id": 2,
              "name": "Parent Topic 1",
              "parentTopic": {
                  "id": 1,
                  "name": "Parent Topic 2",
                  "parentTopic": null
               }
          }
   },
  {
      "id": 4,
      "name": "Important Topic 4",
      "questions": [],
      "topics": [],
      "parentTopic": {
              "id": 2,
              "name": "Parent Topic 1",
              "parentTopic": {
                  "id": 1,
                  "name": "Parent Topic 2",
                  "parentTopic": null
               }
          }
   }
]`

const baseData = JSON.parse(jsonData)


// List of all topics (children and parents)
let topics = []

// Recursive function to add children elements
const listTopics = topic => {
  // Push only if it doesn't already exists to avoid duplicates
  if (!topics.some(e => e.id === topic.id))
    topics.push(topic)

  // The recursive part
  if (topic.parentTopic)
    listTopics(topic.parentTopic)
}

// Ignite
baseData.forEach(listTopics)


// Runs while none of the topics elements has a parent
// You can replace it by `topics.length > 1` if you only have one root topic
while (topics.some(e => e.parentTopic)) {
  const parent = topics.shift()

  topics = topics.filter(topic => {
    if (topic.parentTopic && topic.parentTopic.id === parent.id) {
      if (!parent.topics)
        parent.topics = []

      parent.topics.push(topic)
      return false
    }

    return true
  })

  topics.push(parent)
}

// Print the new tree as JSON with some modifications to avoid circulars between parents and children
console.log(JSON.stringify(topics, (key, value) => key === 'parentTopic' ? (value === null ? null : {
  id: value.id
}) : value, '  '))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.