Home > OS >  Recursive loop to build object
Recursive loop to build object

Time:03-15

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)

CodePudding user response:

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.

CodePudding user response:

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)}))

CodePudding user response:

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

CodePudding user response:

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
  }, [])
}
  • Related