0

I can't figure out how to explain this right, but I wanna make a function that takes this array:

const tasks = [
    {id: 1, goal: 'Clean apartment', parent_id: null},
    {id: 2, goal: 'Clean bathroom', parent_id: 1},
    {id: 3, goal: 'Clean kitchen', parent_id: 1},
    {id: 4, goal: 'Clean sink', parent_id: 2},
    {id: 5, goal: 'Clean shower', parent_id: 2},
    {id: 6, goal: 'Make app', parent_id: null}

]

and generates this object:

{id: 1, goal: 'Clean apartment', parent_id: null, children: [
    {id: 2, goal: 'Clean bathroom', parent_id: 1, children: [
        {id: 4, goal: 'Clean sink', parent_id: 2, children: []},
        {id: 5, goal: 'Clean shower', parent_id: 2, children: []},
    ]},
    {id: 3, goal: 'Clean kitchen', parent_id: 1, children: []},
]},
{id: 6, goal: 'Make app', parent_id: null, children: []}

Edit: So far I made this, but it only returns the first layer of the children:

function addChildren(tasks, id) {
    var task = tasks.find(task => task.id === id)
    var children = tasks.filter(task => task.parent_id === id)
    
    task.children = children
    return task
}

var newTask = addChildren(tasks, 1)
console.log(newTask)

EDIT 2: I tried making the function recursive, but I get an error saying "tasks.find is not a function".

function addChildren(tasks, id) {
    var task = tasks.find(task => task.id === id)
    var children = tasks.filter(task => task.parent_id === id)
    
    task.children = children
    
    task.children.forEach(child => {
        addChildren(child, child.id)
    })
    return task
}

var newTask = addChildren(tasks, 1)
console.log(newTask)
7
  • So make the function! What is stopping you? Can you show us what you tried and tell us where you are stuck? Commented Mar 14, 2022 at 23:48
  • I'm trying! I'm stuck on the second layer. I can look up children of the first task, but I'm not able to make the function recursive. Commented Mar 14, 2022 at 23:49
  • Please edit your question to add the code of your attempt. Without that, we can't help you fix it Commented Mar 14, 2022 at 23:52
  • 1
    Alright, I added the function I've been able to make so far Commented Mar 15, 2022 at 0:04
  • Make the function recursive and call it for each child. Commented Mar 15, 2022 at 0:07

4 Answers 4

1

Make the function recursive and call it for each child.

const tasks = [
    {id: 1, goal: 'Clean apartment', parent_id: null},
    {id: 2, goal: 'Clean bathroom', parent_id: 1},
    {id: 3, goal: 'Clean kitchen', parent_id: 1},
    {id: 4, goal: 'Clean sink', parent_id: 2},
    {id: 5, goal: 'Clean shower', parent_id: 2},
    {id: 6, goal: 'Make app', parent_id: null}

]

function addChildren(tasks, idOrTask) {
    var task = (typeof idOrTask === 'number') ? tasks.find(task => task.id === idOrTask) : idOrTask;
    var children = tasks.filter(t => t.parent_id === task.id);
    
    task.children = children.map(child => addChildren(tasks, child));
    return task
}

var newTask = addChildren(tasks, 1)
console.log(newTask)

I've optimized the function a little to avoid unnecessary calls of find.

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

1 Comment

Thanks! I'm gonna take a look at that "map" function!
1

You can filter those that match a given id, and calculate their children with a recursive call:

const nest = (xs, parent = null) =>
  xs .filter (({parent_id}) => parent_id == parent)
     .map (({id, ...rest}) => ({id, ...rest, children: nest (xs, id)}))
 

const tasks = [{id: 1, goal: 'Clean apartment', parent_id: null}, {id: 2, goal: 'Clean bathroom', parent_id: 1}, {id: 3, goal: 'Clean kitchen', parent_id: 1}, {id: 4, goal: 'Clean sink', parent_id: 2}, {id: 5, goal: 'Clean shower', parent_id: 2}, {id: 6, goal: 'Make app', parent_id: null}]

console .log (nest (tasks))
.as-console-wrapper {max-height: 100% !important; top: 0}

If you no longer want the now-redundant parent_id in the output, you can simply remove it by replacing the map line above with:

    .map (({id, parent_id, ...rest}) => ({id, ...rest, children: nest (xs, id)}))

Comments

0

const tasks = [
    {id: 1, goal: 'Clean apartment', parent_id: null},
    {id: 2, goal: 'Clean bathroom', parent_id: 1},
    {id: 3, goal: 'Clean kitchen', parent_id: 1},
    {id: 4, goal: 'Clean sink', parent_id: 2},
    {id: 5, goal: 'Clean shower', parent_id: 2},
    {id: 6, goal: 'Make app', parent_id: null}

]

function buildTree(root) {
    return {
      goal: root.goal, // Store the goal
      children: tasks
        .filter(task => task.parent_id === root.id) // All tasks with parent_id  = root.id
        .map(child => buildTree(child)) // Recursively collect their children
    }
}

const result = buildTree(tasks[0]) // Build the tree starting with 'Clean apartment'
console.log(result);

You can simplify the logic somewhat by not passing around IDs

Comments

0

Why is recursivity a requirement? You can use this iterative way

function buildTree(tasks) {
  const tasksById = tasks.reduce((acc, task) => ({
    ...acc,
    [task.id]: { ...task }
  }), {})

  return Object.values(tasksById).reduce((tree, task) => {
    const parent = tasksById[task.parent_id]
    if (!parent) {
      return tree.concat(task)
    }

    parent.children = parent.children || []
    parent.children.push(task)
    return tree
  }, [])
}

1 Comment

Because everything that can be done with recursion can be done with iteration, and vice versa, you need some other reason to choose one over the other for a particular problem. I will go with elegance, which often means recursion, unless I find a performance issue with it. Here, note how much simpler the answers using recursion are.

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.