'Get all parents ids from id list in nested object in javascript

So I have an array of objects which have all nested children property. It is in front-end a treeview, which should expand the nodes until selected ones, for each id in a list. To be able to do this, I have to get all the parents ids for each selected id from the list.

For example, my list of checkedKeys ids:

[16787217, 16787245, 16787266, 16787270, 16787272, 16787265, 16787264]

All the checked items represents the ids from the list object:

enter image description here

My object list looks like this:

[
  {
    "id": 11,
    "name": "Forecast source",
    "value": null,
    "children": []
  },
  {
    "id": 2,
    "name": "Item Type",
    "value": null,
    "children": []
  },
  {
    "id": 16787217,
    "name": "Item@Cust",
    "value": null,
    "children": [
      {
        "id": 16787230,
        "name": "Customer",
        "value": null,
        "children": [
          {
            "id": 16787291,
            "name": "Commercial Network",
            "value": null,
            "children": []
          },
          {
            "id": 16787296,
            "name": "Distribution Site",
            "value": null,
            "children": []
          },
          {
            "id": 16787265,
            "name": "Site",
            "value": null,
            "children": []
          }
        ]
      },
      {
        "id": 16787254,
        "name": "Item",
        "value": null,
        "children": [
          {
            "id": 16787294,
            "name": "ABC (Regular)",
            "value": null,
            "children": []
          },
          {
            "id": 16787273,
            "name": "ABC (U)",
            "value": null,
            "children": []
          },
          {
            "id": 16787278,
            "name": "ABC (€)",
            "value": null,
            "children": []
          },
          {
            "id": 16787290,
            "name": "Class",
            "value": null,
            "children": []
          },
          {
            "id": 16787260,
            "name": "Family",
            "value": null,
            "children": [
              {
                "id": 16787264,
                "name": "Product line",
                "value": null,
                "children": []
              }
            ]
          },
          {
            "id": 16787263,
            "name": "Flavour",
            "value": null,
            "children": []
          },
          {
            "id": 16787262,
            "name": "Format",
            "value": null,
            "children": []
          },
          {
            "id": 16787261,
            "name": "Group 1",
            "value": null,
            "children": []
          },
          {
            "id": 16787292,
            "name": "ProdGroup",
            "value": null,
            "children": []
          },
          {
            "id": 16787293,
            "name": "Recipe",
            "value": null,
            "children": []
          },
          {
            "id": 16787288,
            "name": "Sale status",
            "value": null,
            "children": []
          }
        ]
      },
      {
        "id": 16787245,
        "name": "Item@Site",
        "value": null,
        "children": [
          {
            "id": 16787266,
            "name": "Family@Warehouse",
            "value": null,
            "children": []
          }
        ]
      }
    ]
  },
  {
    "id": 3,
    "name": "Lead Time",
    "value": null,
    "children": []
  },
  {
    "id": 5,
    "name": "Levels",
    "value": null,
    "children": []
  },
  {
    "id": 16787268,
    "name": "N1",
    "value": null,
    "children": [
      {
        "id": 16787269,
        "name": "N2",
        "value": null,
        "children": [
          {
            "id": 16787270,
            "name": "N3",
            "value": null,
            "children": [
              {
                "id": 16787271,
                "name": "N4",
                "value": null,
                "children": [
                  {
                    "id": 16787272,
                    "name": "N5",
                    "value": null,
                    "children": [
                      {
                        "id": 33564497,
                        "name": "N6",
                        "value": null,
                        "children": [
                          {
                            "id": 33564498,
                            "name": "N7",
                            "value": null,
                            "children": [
                              {
                                "id": 33564499,
                                "name": "N8",
                                "value": null,
                                "children": [
                                  {
                                    "id": 33564500,
                                    "name": "N9",
                                    "value": null,
                                    "children": []
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "id": 16787286,
    "name": "Op set",
    "value": null,
    "children": []
  }
]

So my problem is that I can't figure out how to get all parents nodes ids recursively until the root level and push them into the expandedKeys array.

I tried to implement a function like this:

this.list.forEach(el => {
  this.setExpandedNodes(el);
});

setExpandedNodes(node: PropertyNode) {
        if (node.children) {
            node.children.forEach(chld => {
                this.checkedKeys.forEach(key => {
                    if (chld.id === key) {
                        this.expandedKeys.push(node.id);
                    } else if (chld.children) {
                        chld.children.forEach(grChld => {
                            this.setExpandedNodes(grChld);
                        });
                    }
                });
            });
        }
    }

But I can't figure out how to get all parent ids starting from each selected id until the root level. Anyone have an idea?



Solution 1:[1]

Thanks to this answer I managed to do it.

getPath(model, id) {
    let path,
        item = model.id ;

    if (!model || typeof model !== 'object') {
        return;
    }

    if (model.id === id) {
        return [item];
    }

    (model.children || []).some(child => (path = this.getPath(child, id)));
    return path && [item, ...path];
}

setExpandedKeys() {
    if (this.checkedKeys && this.checkedKeys.length > 0) {
        this.checkedKeys.forEach(k => {
            this.list.forEach(
                mt => {
                    const result = this.getPath(mt, k);
                    if (result) {
                        this.expandedKeys.push(...result);
                    }
                }
            );
        });
    }
}

this.setExpandedKeys();

I have now all the parents ids until the root.

Solution 2:[2]

A function that returns an array of parent objects may look like this:

Using tree-model-js:

    const getParentsById = (id, data) => {
var TreeModel = require('tree-model')
const tree = new TreeModel()
let path
for (let item of data) {
    const root = tree.parse(item)
    root.walk(function (node) {
        // Halt the traversal by returning false
        if (node.model.id === id) {
            path = node.getPath()
            return false;
        }
    });
}
path.pop()
return path.map(item => item.model)
}

Without using tree-model-js:

    const getParentsById = (id, data) => {
const isFoundChild = (id, data, parents) => {
    if (data.find(item => item.id == id)) {
        return true;
    }
    else {
        for (let item of data) {
            if (item.children.length)
                if (isFoundChild(id, item.children)) {
                    parents.push(item);
                    return true;
                }
        }
        return false;
    }
}

const parents = [];
if (data.find(item => item.id == id))
    return [];
else {
    for (let item of data) {
        if (item.children.length)
            if (isFoundChild(id, item.children, parents)) {
                parents.push(item);
                return parents;
            }
    }
}
}

Next, it is enough to apply map() to the result:

let parentsId = getParentsById(16787296, data).map(item => item.id) 

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yin
Solution 2