Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用JavaScript在树中查找节点_Javascript - Fatal编程技术网

如何使用JavaScript在树中查找节点

如何使用JavaScript在树中查找节点,javascript,Javascript,我有一个和object literal,它本质上是一个没有固定级别数的树。如何在树中搜索特定节点,然后在javascript中高效地找到该节点时返回该节点 基本上我有一个这样的树,我想找到标题为“randomNode_1”的节点 var data = [ { title: 'topNode', children: [ { title: 'node1', children: [ { title: 'randomNode_1'

我有一个和object literal,它本质上是一个没有固定级别数的树。如何在树中搜索特定节点,然后在javascript中高效地找到该节点时返回该节点

基本上我有一个这样的树,我想找到标题为“randomNode_1”的节点

var data = [
{
title: 'topNode',
 children: [
   {
       title: 'node1',
       children: [
       {
           title: 'randomNode_1'
       },
       {   
           title: 'node2',
           children: [
           {
               title: 'randomNode_2',
               children:[
               {   
                   title: 'node2',
                   children: [
                   {
                       title: 'randomNode_3',
                   }]
               }
               ]
           }]
       }]
   }
  ]
 }];

这是基本的递归问题

window.parser = function(searchParam, data) {
  if(data.title != searchParam) {
    returnData = window.parser(searchParam, children)
  } else {
     returnData = data;
  }

  return returnData;
}

你必须使用递归

var currChild = data[0];
function searchTree(currChild, searchString){
     if(currChild.title == searchString){
          return currChild;
     }else if (currChild.children != null){
          for(i=0; i < currChild.children.length; i ++){
               if (currChild.children[i].title ==searchString){
                    return currChild.children[i];
               }else{
                    searchTree(currChild.children[i], searchString);
               }
          }
          return null;
     }
     return null;
}
function searchTree(element, matchingTitle){
     if(element.title == matchingTitle){
          return element;
     }else if (element.children != null){
          var i;
          var result = null;
          for(i=0; result == null && i < element.children.length; i++){
               result = searchTree(element.children[i], matchingTitle);
          }
          return result;
     }
     return null;
}
var currChild=data[0];
函数searchTree(currChild,searchString){
if(currChild.title==searchString){
返回儿童;
}else if(currChild.children!=null){
对于(i=0;i
基于@Ravindra的答案,但使用真正的递归

var currChild = data[0];
function searchTree(currChild, searchString){
     if(currChild.title == searchString){
          return currChild;
     }else if (currChild.children != null){
          for(i=0; i < currChild.children.length; i ++){
               if (currChild.children[i].title ==searchString){
                    return currChild.children[i];
               }else{
                    searchTree(currChild.children[i], searchString);
               }
          }
          return null;
     }
     return null;
}
function searchTree(element, matchingTitle){
     if(element.title == matchingTitle){
          return element;
     }else if (element.children != null){
          var i;
          var result = null;
          for(i=0; result == null && i < element.children.length; i++){
               result = searchTree(element.children[i], matchingTitle);
          }
          return result;
     }
     return null;
}

下面是一个迭代解决方案:

var stack = [], node, ii;
stack.push(root);

while (stack.length > 0) {
    node = stack.pop();
    if (node.title == 'randomNode_1') {
        // Found it!
        return node;
    } else if (node.children && node.children.length) {
        for (ii = 0; ii < node.children.length; ii += 1) {
            stack.push(node.children[ii]);
        }
    }
}

// Didn't find it. Return null.
return null;
var-stack=[],节点,ii;
栈.推(根);
while(stack.length>0){
node=stack.pop();
如果(node.title=='randomNode_1'){
//找到了!
返回节点;
}else if(node.children&&node.children.length){
对于(ii=0;ii
以下内容在我这边起作用:

function searchTree(data, value) {
if(data.title == value) {
    return data;
}
if(data.children && data.children.length > 0) {
    for(var i=0; i < data.children.length; i++) {
        var node = traverseChildren(data.children[i], value);
        if(node != null) {
            return node;
        }
    }
}
return null;
函数搜索树(数据、值){
if(data.title==值){
返回数据;
}
if(data.children&&data.children.length>0){
对于(var i=0;i

}

我的答案是从FishBasketGordo的迭代答案中得到启发的。它稍微复杂一点,但也更加灵活,可以有多个根节点

/**searchs through all arrays of the tree if the for a value from a property
 * @param aTree : the tree array
 * @param fCompair : This function will receive each node. It's upon you to define which 
                     condition is necessary for the match. It must return true if the condition is matched. Example:
                        function(oNode){ if(oNode["Name"] === "AA") return true; }
 * @param bGreedy? : us true to do not stop after the first match, default is false
 * @return an array with references to the nodes for which fCompair was true; In case no node was found an empty array
 *         will be returned
*/
var _searchTree = function(aTree, fCompair, bGreedy){
    var aInnerTree = []; // will contain the inner children
    var oNode; // always the current node
    var aReturnNodes = []; // the nodes array which will returned

    // 1. loop through all root nodes so we don't touch the tree structure
    for(keysTree in aTree) {
        aInnerTree.push(aTree[keysTree]);
    }
    while(aInnerTree.length > 0) {
        oNode = aInnerTree.pop();
        // check current node
        if( fCompair(oNode) ){
            aReturnNodes.push(oNode);
            if(!bGreedy){
                return aReturnNodes;
            }
        } else { // if (node.children && node.children.length) {
            // find other objects, 1. check all properties of the node if they are arrays
            for(keysNode in oNode){
                // true if the property is an array
                if(oNode[keysNode] instanceof Array){
                    // 2. push all array object to aInnerTree to search in those later
                    for (var i = 0; i < oNode[keysNode].length; i++) {
                        aInnerTree.push(oNode[keysNode][i]);
                    }
                }
            }
        }
    }
    return aReturnNodes; // someone was greedy
}
如果要查找具有此标题的所有节点,只需切换bGreedy参数:

var foundNodes = _searchTree(data, function(oNode){ if(oNode["title"] === "randomNode_3") return true; }, true);
console.log("NodeS with title found: ");
console.log(foundNodes);

此函数是通用的,并进行递归搜索。 无论输入树是对象(单根)还是对象数组(多根对象),这都无关紧要。您可以配置在树对象中保存子数组的prop name

// Searches items tree for object with specified prop with value
// 
// @param {object} tree nodes tree with children items in nodesProp[] table, with one (object) or many (array of objects) roots
// @param {string} propNodes name of prop that holds child nodes array
// @param {string} prop name of searched node's prop
// @param {mixed} value value of searched node's  prop
// @returns {object/null} returns first object that match supplied arguments (prop: value) or null if no matching object was found

function searchTree(tree, nodesProp, prop, value) {
  var i, f = null; // iterator, found node
  if (Array.isArray(tree)) { // if entry object is array objects, check each object
    for (i = 0; i < tree.length; i++) {
      f = searchTree(tree[i], nodesProp, prop, value);
      if (f) { // if found matching object, return it.
        return f;
      }
    }
  } else if (typeof tree === 'object') { // standard tree node (one root)
    if (tree[prop] !== undefined && tree[prop] === value) {
      return tree; // found matching node
    }
  }
  if (tree[nodesProp] !== undefined && tree[nodesProp].length > 0) { // if this is not maching node, search nodes, children (if prop exist and it is not empty)
    return searchTree(tree[nodesProp], nodesProp, prop, value);
  } else {
    return null; // node does not match and it neither have children
  }
}

它适用于(粘贴代码)

一种灵活的递归解决方案,适用于任何树

// predicate: (item) => boolean
// getChildren: (item) => treeNode[]
searchTree(predicate, getChildren, treeNode) {
        function search(treeNode) {
            if (!treeNode) {
                return undefined;
            }

            for (let treeItem of treeNode) {
                if (predicate(treeItem)) {
                    return treeItem;
                }

                const foundItem = search(getChildren(treeItem));

                if (foundItem) {
                    return foundItem;
                }
            }
        }

        return search(treeNode);
    }

这是一个使用堆栈方法的迭代函数,它受到了一些ES2015语法的启发,但利用了这些语法来缩短时间

由于这个问题已经被浏览了很多次,我决定更新我的答案,同时提供一个带有参数的函数,使其更加灵活:

函数搜索(树,值,键='id',反向=false){
常量堆栈=[树[0]]
while(堆栈长度){
常量节点=堆栈[反转?'pop':'shift']()
if(节点[键]==值)返回节点
node.children&&stack.push(…node.children)
}
返回空
}
通过这种方式,现在可以传递数据
本身、要搜索的所需
,以及可以具有所需值的属性

search(data, 'randomNode_2', 'title')
最后,我的原始答案使用了
Array.pop
,这导致在多次匹配的情况下匹配最后一项。事实上,有些事情可能真的很让人困惑。受此启发,我现在使用了
Array.shift
,因此先进先出行为是默认行为

如果您确实想要旧的后进先出行为,我提供了一个额外的参数
反向

search(data, 'randomNode_2', 'title', true)

在树中查找元素的所有父级

let objects = [{
      id: 'A',
      name: 'ObjA',
      children: [
        {
          id: 'A1',
          name: 'ObjA1'
        },
        {
          id: 'A2',
          name: 'objA2',
          children: [
            {
              id: 'A2-1',
              name: 'objA2-1'
            },
            {
              id: 'A2-2',
              name: 'objA2-2'
            }
          ]
        }
      ]
    },
    {
      id: 'B',
      name: 'ObjB',
      children: [
        {
          id: 'B1',
          name: 'ObjB1'
        }
      ]
    }
    ];

let docs = [
  {

    object: {
      id: 'A',
      name: 'docA'
    },
    typedoc: {
      id: 'TD1',
      name: 'Typde Doc1'
    }
  },
  {

    object: {
      id: 'A',
      name: 'docA'
    },
    typedoc: {
      id: 'TD2',
      name: 'Typde Doc2'
    }
  },
  {

    object: {
      id: 'A1',
      name: 'docA1'
    },
    typedoc: {
      id: 'TDx1',
      name: 'Typde Doc x1'
    }
  },
  {

    object: {
      id: 'A1',
      name: 'docA1'
    },
    typedoc: {
      id: 'TDx2',
      name: 'Typde Doc x1'
    }
  },
  {

    object: {
      id: 'A2',
      name: 'docA2'
    },
    typedoc: {
      id: 'TDx2',
      name: 'Type de Doc x2'
    }
  },
  {

    object: {
      id: 'A2-1',
      name: 'docA2-1'
    },
    typedoc: {
      id: 'TDx2-1',
      name: 'Type de Docx2-1'
    },
  },
  {

    object: {
      id: 'A2-2',
      name: 'docA2-2'
    },
    typedoc: {
      id: 'TDx2-2',
      name: 'Type de Docx2-2'
    },
  },
  {

    object: {
      id: 'B',
      name: 'docB'
    },
    typedoc: {
      id: 'TD1',
      name: 'Typde Doc1'
    }
  },
  {

    object: {
      id: 'B1',
      name: 'docB1'
    },
    typedoc: {
      id: 'TDx1',
      name: 'Typde Doc x1'
    }

  }
];



function buildAllParents(doc, objects) {
    for (let o = 0; o < objects.length; o++) {
      let allParents = [];
      let getAllParents = (o, eleFinded) => {
        if (o.id === doc.object.id) {
          doc.allParents = allParents;
          eleFinded = true;
          return { doc, eleFinded };
        }
        if (o.children) {
          allParents.push(o.id);
          for (let c = 0; c < o.children.length; c++) {
            let { eleFinded, doc } = getAllParents(o.children[c], eleFinded);
            if (eleFinded) {
              return { eleFinded, doc };
            } else {
              continue;
            }
          }

        }
        return { eleFinded };
      };
      if (objects[o].id === doc.object.id) {
        doc.allParents = [objects[o].id];
        return doc;
      } else if (objects[o].children) {
        allParents.push(objects[o].id);
        for (let c = 0; c < objects[o].children.length; c++) {
          let eleFinded = null;`enter code here`
          let res = getAllParents(objects[o].children[c], eleFinded);
          if (res.eleFinded) {
            return res.doc;
          } else {
            continue;
          }
        }
      }

    }
  }

docs = docs.map(d => buildAllParents(d, objects`enter code here`))
let对象=[{
id:'A',
名称:“ObjA”,
儿童:[
{
id:'A1',
名称:“ObjA1”
},
{
id:'A2',
名称:“objA2”,
儿童:[
{
id:'A2-1',
名称:“objA2-1”
},
{
id:'A2-2',
名称:“objA2-2”
}
]
}
]
},
{
id:'B',
名称:“ObjB”,
儿童:[
{
id:'B1',
名称:“ObjB1”
}
]
}
];
让文档=[
{
对象:{
id:'A',
名称:“docA”
},
类型文档:{
id:'TD1',
名称:“Typde Doc1”
}
},
{
对象:{
id:'A',
名称:“docA”
},
类型文档:{
id:'TD2',
名称:“Typde Doc2”
}
},
{
对象:{
id:'A1',
名称:'docA1'
},
类型文档:{
id:'TDx1',
名称:“类型文件x1”
}
},
{
对象:{
id:'A1',
名称:'docA1'
},
类型文档:{
id:'TDx2',
名称:“类型文件x1”
}
},
{
对象:{
id:'A2',
名称:“docA2”
},
类型文档:{
id:'TDx2',
名称:'Type de Doc x2'
}
},
{
对象:{
id:'A2-1',
名称:“docA2-1”
},
类型文档:{
id:'TDx2-1',
名称:“类型de Docx2-1”
},
},
{
对象:{
id:'A2-2',
名称:“docA2-2”
},
类型文档:{
id:'TDx2-2',
名称:'Type de Docx2-2'
},
},
{
对象:{
id:'B',
名称:“docB”
},
类型文档:{
id:'TD1',
名称:“Typde Doc1”
}
},
{
对象:{
id:'B1',
名称:“docB1”
},
类型文档:{
id:'TDx1',
名称:“类型文件x1”
}
}
];
函数buildAllParents(文档、对象){
for(设o=0;o{
if(o.id==doc.object.id){
doc.allParents=allParents;
Elefined=正确;
返回{doc,elefined};
}
如果(o.儿童){
所有家长。推送(o.id);
对于(让c
let objects = [{
      id: 'A',
      name: 'ObjA',
      children: [
        {
          id: 'A1',
          name: 'ObjA1'
        },
        {
          id: 'A2',
          name: 'objA2',
          children: [
            {
              id: 'A2-1',
              name: 'objA2-1'
            },
            {
              id: 'A2-2',
              name: 'objA2-2'
            }
          ]
        }
      ]
    },
    {
      id: 'B',
      name: 'ObjB',
      children: [
        {
          id: 'B1',
          name: 'ObjB1'
        }
      ]
    }
    ];

let docs = [
  {

    object: {
      id: 'A',
      name: 'docA'
    },
    typedoc: {
      id: 'TD1',
      name: 'Typde Doc1'
    }
  },
  {

    object: {
      id: 'A',
      name: 'docA'
    },
    typedoc: {
      id: 'TD2',
      name: 'Typde Doc2'
    }
  },
  {

    object: {
      id: 'A1',
      name: 'docA1'
    },
    typedoc: {
      id: 'TDx1',
      name: 'Typde Doc x1'
    }
  },
  {

    object: {
      id: 'A1',
      name: 'docA1'
    },
    typedoc: {
      id: 'TDx2',
      name: 'Typde Doc x1'
    }
  },
  {

    object: {
      id: 'A2',
      name: 'docA2'
    },
    typedoc: {
      id: 'TDx2',
      name: 'Type de Doc x2'
    }
  },
  {

    object: {
      id: 'A2-1',
      name: 'docA2-1'
    },
    typedoc: {
      id: 'TDx2-1',
      name: 'Type de Docx2-1'
    },
  },
  {

    object: {
      id: 'A2-2',
      name: 'docA2-2'
    },
    typedoc: {
      id: 'TDx2-2',
      name: 'Type de Docx2-2'
    },
  },
  {

    object: {
      id: 'B',
      name: 'docB'
    },
    typedoc: {
      id: 'TD1',
      name: 'Typde Doc1'
    }
  },
  {

    object: {
      id: 'B1',
      name: 'docB1'
    },
    typedoc: {
      id: 'TDx1',
      name: 'Typde Doc x1'
    }

  }
];



function buildAllParents(doc, objects) {
    for (let o = 0; o < objects.length; o++) {
      let allParents = [];
      let getAllParents = (o, eleFinded) => {
        if (o.id === doc.object.id) {
          doc.allParents = allParents;
          eleFinded = true;
          return { doc, eleFinded };
        }
        if (o.children) {
          allParents.push(o.id);
          for (let c = 0; c < o.children.length; c++) {
            let { eleFinded, doc } = getAllParents(o.children[c], eleFinded);
            if (eleFinded) {
              return { eleFinded, doc };
            } else {
              continue;
            }
          }

        }
        return { eleFinded };
      };
      if (objects[o].id === doc.object.id) {
        doc.allParents = [objects[o].id];
        return doc;
      } else if (objects[o].children) {
        allParents.push(objects[o].id);
        for (let c = 0; c < objects[o].children.length; c++) {
          let eleFinded = null;`enter code here`
          let res = getAllParents(objects[o].children[c], eleFinded);
          if (res.eleFinded) {
            return res.doc;
          } else {
            continue;
          }
        }
      }

    }
  }

docs = docs.map(d => buildAllParents(d, objects`enter code here`))
const findInTree = (node, childrenKey, key, value,  additionalKey?, additionalValue?) => {
  let found = null;
  if (additionalKey && additionalValue) {
    found = node[childrenKey].find(x => x[key] === value && x[additionalKey] === additionalValue);
  } else {
    found = node[childrenKey].find(x => x[key] === value);
  }
  if (typeof(found) === 'undefined') {
    for (const item of node[childrenKey]) {
      if (typeof(found) === 'undefined' && item[childrenKey] && item[childrenKey].length > 0) {
        found = findInTree(item, childrenKey, key, value, additionalKey, additionalValue);
      }
    }
  }
  return found;
};

export { findInTree };
getParentNode(nodeName, nodeValue, rootNode) {
    const queue= [ rootNode ]
    while (queue.length) {
        const node = queue.shift()
        if (node[nodeName] === nodeValue) {
            return node
        } else if (node instanceof Object) {
            const children = Object.values(node)
            if (children.length) {
                queue.push(...children)
            }
        }
    }
    return null
}
getParentNode('title', 'randomNode_1', data[0])
const deepSearch = (data, value, key = 'title', sub = 'children', tempObj = {}) => {
    if (value && data) {
        data.find((node) => {
            if (node[key] == value) {
                tempObj.found = node;
                return node;
            }
            return deepSearch(node[sub], value, key, sub, tempObj);
        });
        if (tempObj.found) {
            return tempObj.found;
        }
    }
    return false;
};

const result = deepSearch(data, 'randomNode_1', 'title', 'children');
function searchTree(
  tree: Record<string, any>[],
  value: unknown,
  key = 'value',
  withChildren = false,
) {
  let result = null;
  if (!Array.isArray(tree)) return result;

  for (let index = 0; index < tree.length; index += 1) {
    const stack = [tree[index]];
    while (stack.length) {
      const node = stack.shift()!;
      if (node[key] === value) {
        result = node;
        break;
      }
      if (node.children) {
        stack.push(...node.children);
      }
    }
    if (result) break;
  }
  if (withChildren !== true) {
    delete result?.children;
  }

  return result;
}