搜索嵌套对象并返回整个路径

时间:2018-01-29 10:41:31

标签: javascript json object underscore.js lodash

我有以下带有n级孩子的JavaScript,并且想要搜索id,如果任何项目来自匹配id,则需要将对象从root返回到匹配项目。

我希望将找到的项目的整个层次结构从root返回到包含它的孩子的对象。

我尝试使用lodash和下划线,但找不到简单的解决方案。

input: {
  "children": [{
      "name": "Home",
      "title": "Home",
      "id": "home1",
      "children": []
    },
    {
      "name": "BUSINESS AND ROLE SPECIFIC",
      "title": "BUSINESS AND ROLE SPECIFIC",
      "id": "BAR1",
      "children": [{
        "name": "Global Businesses",
        "title": "Global Businesses",
        "id": "GB1",
        "children": [{
          "name": "Commercial Banking",
          "title": "Commercial Banking",
          "id": "CB1",
          "children": [{
            "name": "FLAGSHIP PROGRAMMES",
            "title": "FLAGSHIP PROGRAMMES",
            "id": "FG1",
            "children": []
          }]
        }]
      }]
    },
    {
      "name": "RISK MANAGEMENT",
      "title": "RISK MANAGEMENT",
      "id": "RM1",
      "children": []
    }
  ]
}

Search: {
  id: 'FG1'
}

return :{
  "name": "BUSINESS AND ROLE SPECIFIC",
  "title": "BUSINESS AND ROLE SPECIFIC",
  "id": "BAR1",
  "children": [{
    "name": "Global Businesses",
    "title": "Global Businesses",
    "id": "GB1",
    "children": [{
      "name": "Commercial Banking",
      "title": "Commercial Banking",
      "id": "CB1",
      "children": [{
        "name": "FLAGSHIP PROGRAMMES",
        "title": "FLAGSHIP PROGRAMMES",
        "id": "FG1",
        "children": [{}]
      }]
    }]
  }]
}

3 个答案:

答案 0 :(得分:2)

您可以使用此功能:



function findChild(obj, condition) {
    if (Object.entries(condition).every( ([k,v]) => obj[k] === v )) {
        return obj;
    }
    for (const child of obj.children || []) {
        const found = findChild(child, condition);
        // If found, then add this node to the ancestors of the result
        if (found) return Object.assign({}, obj, { children: [found] });
    }
}
// Sample data
var input = { "children": [{ "name": "Home", "title": "Home", "id": "home1", "children": [] }, { "name": "BUSINESS AND ROLE SPECIFIC", "title": "BUSINESS AND ROLE SPECIFIC", "id": "BAR1", "children": [{ "name": "Global Businesses", "title": "Global Businesses", "id": "GB1", "children": [{ "name": "Commercial Banking", "title": "Commercial Banking", "id": "CB1", "children": [{ "name": "FLAGSHIP PROGRAMMES", "title": "FLAGSHIP PROGRAMMES", "id": "FG1", "children": [] }] }] }] }, { "name": "RISK MANAGEMENT", "title": "RISK MANAGEMENT", "id": "RM1", "children": [] } ]},
    search = { id: 'FG1' };

console.log(findChild(input, search));

.as-console-wrapper { max-height: 100% !important; top: 0; }




你也可以使用它来搜索多个条件,这些条件必须同时为真:

search = { "name": "Global Businesses", "title": "Global Businesses" };

...会为您提供具有指定名称和标题的对象。

后续问题

你在评论中提问:

  

是否可以提供数字以便不在输入中删除给定节点的子节点。喜欢,

const donotRemoveChildNode = 2; 
console.log(findChild(input, search, donotRemoveChildNode )); 
     

...所以如果匹配条件,它不会删除特定节点的子节点吗?

     

在这里,如果我们搜索{ id: 'FG1'}并提供donotRemoveChildNode = 2,则不会删除"商业银行"的第一级孩子。

我想说donotRemoveChildNode必须是3,因为"商业银行"的祖先层次结构中有三个级别的children数组。节点。值为0将显示最顶层children属性的第一级子项。

以下是额外参数的工作原理 - 我在数据中添加了一些记录来说明输出的差异:



function findChild(obj, condition, removeChildNodesBefore = Infinity) {
    if (Object.entries(condition).every( ([k,v]) => obj[k] === v )) {
        return obj;
    }
    for (const child of obj.children || []) {
        let found = findChild(child, condition, removeChildNodesBefore - 1);
        if (found) {
            return Object.assign({}, obj, { 
                children: removeChildNodesBefore <= 0 
                    ? obj.children.map( sibling => 
                        sibling == child ? found 
                                         : Object.assign({}, sibling, {children: []}) 
                      )
                    : [found]
            });
        }
    }
}

var input = { "children": [{ "name": "Home", "title": "Home", "id": "home1", "children": [] }, { "name": "BUSINESS AND ROLE SPECIFIC", "title": "BUSINESS AND ROLE SPECIFIC", "id": "BAR1", "children": [{ "name": "Global Businesses", "title": "Global Businesses", "id": "GB1", "children": [{ "name": "test", "title": "test", "id": "xxx", "children": [{ "name": "testDeep", "title": "test", "id": "deep", "children": []}]}, { "name": "Commercial Banking", "title": "Commercial Banking", "id": "CB1", "children": [{ "name": "test", "title": "test", "id": "yyy", "children": []}, { "name": "FLAGSHIP PROGRAMMES", "title": "FLAGSHIP PROGRAMMES", "id": "FG1", "children": [] }] }] }] }, { "name": "RISK MANAGEMENT", "title": "RISK MANAGEMENT", "id": "RM1", "children": [] } ]},
    search = { id: 'FG1' }

console.log(findChild(input, search, 3));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 1 :(得分:1)

function getBranch(branches, leaf_id)
{   
    var result_branch = null;

    branches.some(function(branch, idx) {
        if (branch.id == leaf_id) {
            result_branch = Object.assign({}, branch);
            result_branch.children.forEach(function(child, idx) {
                delete result_branch.children[idx].children;
            });

            return true;
        } else {
            let target_branch = getBranch(branch.children, leaf_id);

            if (target_branch) {
                result_branch = Object.assign({}, branch);
                delete result_branch.children
                result_branch.children = [target_branch];

                return true;    
            }
        }

        return false;
    });

    return result_branch;
}

console.log(getBranch(input.children, 'GB1'));

答案 2 :(得分:0)

一种方法是首先循环根子项,然后创建另一个函数以查看其中任何子项中是否存在Id。

var data = {
  "children": [{
      "name": "Home",
      "title": "Home",
      "id": "home1",
      "children": []
    },
    {
      "name": "BUSINESS AND ROLE SPECIFIC",
      "title": "BUSINESS AND ROLE SPECIFIC",
      "id": "BAR1",
      "children": [{
        "name": "Global Businesses",
        "title": "Global Businesses",
        "id": "GB1",
        "children": [{
          "name": "Commercial Banking",
          "title": "Commercial Banking",
          "id": "CB1",
          "children": [{
            "name": "FLAGSHIP PROGRAMMES",
            "title": "FLAGSHIP PROGRAMMES",
            "id": "FG1",
            "children": []
          }]
        }]
      }]
    },
    {
      "name": "RISK MANAGEMENT",
      "title": "RISK MANAGEMENT",
      "id": "RM1",
      "children": []
    }
  ]
};

function hasId( id, data ) {
  if (data.id === id) return true;
  if (data.children) {
    for (const child of data.children) {
      if (hasId( id, child)) return true;
    }
  }
  return false;
}

function search( id, data ) {
  for (const child of data.children) {
    if (hasId(id, child)) return child;
  }
  return null;
}

console.log(search( "FG1", data ));