0

I'm trying to create a tree component. But I dont know how insert a item recursively inside a tree.

Each item its created dinamically and I would like instert/create an item/branch of the tree in each level. Problem is that when I cant insert items on level 3.

For example, I have this code generated:

[{
    "id": 0,
    "active": false,
    "favorite": false,
    "level": 0,
    "title": "New Branch 0",
    "editable": false,
    "checkbox": false,
    "checkboxEnabled": false,
    "children": []
}, {
    "id": 1,
    "active": false,
    "favorite": false,
    "level": 0,
    "title": "New Branch 1",
    "editable": false,
    "checkbox": false,
    "checkboxEnabled": false,
    "children": [{
        "id": 3,
        "parentId": 1,
        "active": false,
        "favorite": false,
        "level": 1,
        "title": "New Branch 3",
        "editable": false,
        "checkbox": false,
        "checkboxEnabled": false,
        "children": []
    }, {
        "id": 4,
        "parentId": 1,
        "active": false,
        "favorite": false,
        "level": 1,
        "title": "New Branch 4",
        "editable": false,
        "checkbox": false,
        "checkboxEnabled": false,
        "children": []
    }]
}, {
    "id": 2,
    "active": false,
    "favorite": false,
    "level": 0,
    "title": "New Branch 2",
    "editable": false,
    "checkbox": false,
    "checkboxEnabled": false,
    "children": []
}]

And I'm trying to create a method that insert, for example inside children of New Branch 4 or his children. And that return updated array.

This is my code:

const onCreateChildren = () => {
    let newBranch: IBranch = generateBranch(numBranchs);
    newBranch = Object.assign({}, newBranch, {
        level: level + 1,
        parentId: branch.id,
    });
    let copyChildren: Array<IBranch> = children.slice();
    copyChildren.push(newBranch);
    const updatedBranch: IBranch = Object.assign({}, branch, {
        children: copyChildren,
    });
    if (parentId === undefined) {
        const index: number = branchs.findIndex( (b: IBranch) => b.id === branch.id);
        const updatedBranchs: Array<IBranch> = Object.assign([], branchs, {
            [index]: updatedBranch,
        });
        onUpdateBranchs(updatedBranchs);
    } else {
        const updatedBranchs: any = addBranchInTree(branchs, newBranch);
        onUpdateBranchs(updatedBranchs);
    }

    onUpdateNumBranchs(numBranchs+1);
};

Why doenst work always?? Can someone help me?? Other ideas??

export const addBranchInTree = (branchs: any, branch: IBranch): any => {
    return Array.isArray(branchs)
        ? branchs.map((o: IBranch) => addBranchInTree(o, branch))
        : findNode(branchs, branch);
};

const findNode = (branchs: any, branch: any): any => {
    if (branchs.children.length > 0) {
        branchs.children.forEach((child: any) => {
            return findNode(child, branch);
        });
    }

    if (!Array.isArray(branchs)) {
        if (branchs.parentId === branch.parentId - 1) {
            return branchs.children.push(branch);
        }
    }

    return branchs;
};
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
Javier
  • 1,975
  • 3
  • 24
  • 46
  • How do you want to choose the branch to append to? Does the new one have a parentId that should match one of your existing branch's id? If so and if it's not there, do you want to insert at the root? – Scott Sauyet Jun 03 '20 at 17:40
  • @Javier, I recommend you see [this Q&A](https://stackoverflow.com/q/57933657/633183) which broadens the context of your problem. This applies to your other question from earlier today as well. – Mulan Jun 03 '20 at 20:54
  • 1
    @Thankyou: I'd forgotten that one, although I knew we'd dealt with similar ones before. I added the recursion tag because (1) it's appropriate, and (2) I thought it might bring the attention of some of the smart people who spend time there. Looks like it worked! – Scott Sauyet Jun 03 '20 at 21:07

1 Answers1

2

Similarly to how we did it in an earlier question, we can recursively traverse the object, looking for the item whose id matches our new item's parentId and append the new element to its list of children.

There is a little extra complexity in dealing with an item that has no parentId, which I'm assuming should go on the root. Essentially, if we're in an array and our element has no parentId, we assume we can just append it. This should only happen on the root, if the original data is an array.

const addElement = (obj, el) => 
  Array .isArray (obj) 
    ? 'parentId' in el 
      ? obj .map (o => addElement (o, el))
      : [...obj, el]
  : obj .id === el .parentId 
    ? {...obj, children: [...(obj.children || []), el]}
  : // else
    {...obj, ...('children' in obj ? {children: addElement (obj.children, el)} : {})}

const data = [
  {id: 1, title: 'foo', children: [
    {id: 11, parentId: 1, title: 'bar',},
    {id: 12, parentId: 1, title: 'baz', children: [
      {id: 121, parentId: 12, title: 'qux'},
      {id: 122, parentId: 12, title: 'quz'}
    ]},
    {id: 13, parentId: 1, title: 'corge'}
  ]},
  {id: 2, title: 'grault'}
];

const newItem = {id: 123, parentId: 12, title: 'new element here'}
console .log (addElement (data, newItem))

const motherlessChild = {id: 99, title: 'root element here -- no parentId'}
console .log (addElement (data, motherlessChild))
.as-console-wrapper {min-height: 100% !important; top: 0}

Note (and I didn't point this out on the earlier question) that this does not mutate your data object, returning instead an entirely new one. We also don't deal with the case that the new element has no parentId, or that the tree does not include an element with an id matching the new element's parentId. Those are left as exercises for the reader.

It the matching used above -- checking that the id matches the new element's parentId -- is not correct, could you explain how you do want to find the correct spot to insert it, please?


One other point, and please take this as the constructive criticism that's intended: You've been around StackOverflow for a while. If you haven't read the help center's discussion on creating a minimal, reproducible example, please do so. It seems there is too much code above unrelated to the question you're asking. Especially note your sample data. I was hoping you might get the idea from my earlier answer, but it seems you didn't. You should be able to describe this problem with nodes that have an id, a parentId, children where relevant, and only one more property to demonstrate those parts that aren't specifically noted elsewhere (I used title, but anything would do.) It makes it much easier to answer questions when the extraneous matter has been removed.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • And how could be remove and item? Because if not has children... only I have to find parent and remove selected children but if selected item has children I have to update all parentId of children and remove selected item, and add all children to the parent – Javier Jun 04 '20 at 07:44
  • I've created a question: https://stackoverflow.com/questions/62189613/remove-item-recusively-from-tree – Javier Jun 04 '20 at 07:58