2013-10-20 46 views
4

我有很深的JSON对象的数组,看起来像类似于这样:JavaScript的:如何筛选深JSON对象

var hierarchy = [ 
    { 
    "title": "category 1", 
    "children": [ 
     {"title": "subcategory 1", 
     "children": [ 
      {"id": 1, "title": "name 1"}, 
      {"id": 2, "title": "name 2"}, 
      {"id": 3, "title": "name 3"} 
     ] 
     }, 
     {"title": "subcategory 2", 
     "children": [ 
      {"id": 1, "title": "name 4"} 
     ] 
     } 
    ] 
    }, 
    { 
    "title": "category 2", 
    "children": [etc. - shortened for brevity] 
    } 
]; 

所以基本上它是一个层次 - 有可以有包含对象子类别的类别有一些ID和名字。我也有相关的最深层次水平(无子对象)的ID的数组和我需要以这样的方式,包含定义的对象只(分)类别保持过滤此组对象。

因此,例如,如果我有含有两个ID的数组:

var IDs = [2, 3]; 

结果将是:

var hierarchy = [ 
    { 
    "title": "category 1", 
    "children": [ 
     {"title": "subcategory 1", 
     "children": [ 
      {"id": 2, "title": "name 2"}, 
      {"id": 3, "title": "name 3"} 
     ] 
     } 
    ] 
    } 
]; 

即整体,除去整个“类别2”对象,整个'子类别2'被删除,ID为'1'的对象被删除。

问题是这些对象的深度是可变的和未知的 - 一些对象没有孩子,有些孩子也有孩子等,任何子类别本身都可以有一个子类别,我基本上需要找到对象没有儿童定义了身份证,并且保留了每个人的全部路径。

谢谢。

+1

我强烈推荐这个[下划线](http://underscorejs.org/)(或Lodash) - 这会让你的生活变得更加简单 – Bojangles

+0

@Bojangles - 下划线很棒,但具体问题到底如何帮助我?我可能会错过一些东西,但我没有看到任何下划线方法。谢谢。 – keepsea

+0

对不起,我在我的手机上,并没有正确地阅读这个问题。我看到很多复杂的对象迭代的瞬间想到下划线 – Bojangles

回答

5

基本上,执行你的树调用每个节点上的一个回调函数的深度优先遍历。如果该节点是叶节点它的ID出现在列表中,然后克隆,导致该叶子的枝,但不要再克隆一个已经克隆分支的任何部分。

一旦你建立你的树的部分过滤副本,你需要清理原。为了记账目的,我改变了原来的树 - 追踪哪些分支已经被克隆。

编辑:修改后的代码过滤的树木,而不是只是一个单一的树

var currentPath = []; 

function depthFirstTraversal(o, fn) { 
    currentPath.push(o); 
    if(o.children) { 
     for(var i = 0, len = o.children.length; i < len; i++) { 
      depthFirstTraversal(o.children[i], fn); 
     } 
    } 
    fn.call(null, o, currentPath); 
    currentPath.pop(); 
} 

function shallowCopy(o) { 
    var result = {}; 
    for(var k in o) { 
     if(o.hasOwnProperty(k)) { 
      result[k] = o[k]; 
     } 
    } 
    return result; 
} 

function copyNode(node) { 
    var n = shallowCopy(node); 
    if(n.children) { n.children = []; } 
    return n; 
} 

function filterTree(root, ids) { 
    root.copied = copyNode(root); // create a copy of root 
    var filteredResult = root.copied; 

    depthFirstTraversal(root, function(node, branch) { 
     // if this is a leaf node _and_ we are looking for its ID 
     if(!node.children && ids.indexOf(node.id) !== -1) { 
      // use the path that the depthFirstTraversal hands us that 
      // leads to this leaf. copy any part of this branch that 
      // hasn't been copied, at minimum that will be this leaf 
      for(var i = 0, len = branch.length; i < len; i++) { 
       if(branch[i].copied) { continue; } // already copied 

       branch[i].copied = copyNode(branch[i]); 
       // now attach the copy to the new 'parellel' tree we are building 
       branch[i-1].copied.children.push(branch[i].copied); 
      } 
     } 
    }); 

    depthFirstTraversal(root, function(node, branch) { 
     delete node.copied; // cleanup the mutation of the original tree 
    }); 

    return filteredResult; 
} 

function filterTreeList(list, ids) { 
    var filteredList = []; 
    for(var i = 0, len = list.length; i < len; i++) { 
     filteredList.push(filterTree(list[i], ids)); 
    } 
    return filteredList; 
} 

var hierarchy = [ /* your data here */ ]; 
var ids = [1,3]; 

var filtered = filterTreeList(hierarchy, ids); 
+0

的谢谢麦克,但我不知道我得到这个是如何工作的。特别是你创建root的那个部分。复制 - 根应该是数据层次结构的顶层,这是一个包含多个对象的数组,其中有多个子对象,对吧?我可能错过了一些东西。 – keepsea

+0

再次检查您的代码我看到您正在处理树的列表,而我的代码处理单个树。将代码包装在for循环中以遍历列表应该这样做。看到我上面的编辑。 –

+0

这很聪明,谢谢。 – keepsea

0

列表虽然这是一个老问题,我会添加我的2美分。解决方案需要通过循环,子循环等直接进行迭代,然后比较ID并生成结果对象。我有纯JavaScript和jQuery解决方案。虽然纯JavaScript工程上面的例子中,我会建议jQuery的解决方案,因为它是更通用,并执行对象的“深层复制”,如果你有,你会不会遇到的bug庞大而复杂的对象。

function jsFilter(idList){ 
 
    var rsltHierarchy=[]; 
 
    for (var i=0;i<hierarchy.length;i++) { 
 
    var currCatg=hierarchy[i]; 
 
    var filtCatg={"title":currCatg.title, "children":[]}; 
 
    for (var j=0;j<currCatg.children.length;j++) { 
 
    \t var currSub=currCatg.children[j]; 
 
    \t var filtSub={"title":currSub.title, "children":[]} 
 
    \t for(var k=0; k<currSub.children.length;k++){ 
 
    \t \t if(idList.indexOf(currSub.children[k].id)!==-1) 
 
    \t \t filtSub.children.push({"id":currSub.children[k].id, "title":currSub.children[k].title}); 
 
    \t } 
 
    \t if(filtSub.children.length>0) 
 
    \t \t filtCatg.children.push(filtSub); 
 
    } 
 
    if(filtCatg.children.length>0) 
 
    \t rsltHierarchy.push(filtCatg); 
 
    } 
 
    return rsltHierarchy; 
 
} 
 

 
function jqFilter(idList){ 
 
    var rsltHierarchy=[]; 
 
    $.each(hierarchy, function(index,currCatg){ 
 
     var filtCatg=$.extend(true, {}, currCatg); 
 
     filtCatg.children=[]; 
 
    \t $.each(currCatg.children, function(index,currSub){ 
 
     var filtSub=$.extend(true, {}, currSub); 
 
    \t filtSub.children=[]; 
 
    \t $.each(currSub.children, function(index,currSubChild){ 
 
    \t \t if(idList.indexOf(currSubChild.id)!==-1) 
 
    \t \t filtSub.children.push($.extend(true, {}, currSubChild)); 
 
     }); 
 
    \t if(filtSub.children.length>0) 
 
    \t \t filtCatg.children.push(filtSub); 
 
     }); 
 
     if(filtCatg.children.length>0) 
 
    \t rsltHierarchy.push(filtCatg); 
 
    }); 
 
    return rsltHierarchy; 
 
} 
 

 
//Now test the functions... 
 
var hierarchy = eval("("+document.getElementById("inp").value+")"); 
 
var IDs = eval("("+document.getElementById("txtBoxIds").value+")"); 
 

 
document.getElementById("oupJS").value=JSON.stringify(jsFilter(IDs)); 
 
$(function() { 
 
    $("#oupJQ").text(JSON.stringify(jqFilter(IDs))); 
 
});
#inp,#oupJS,#oupJQ {width:400px;height:100px;display:block;clear:all} 
 
#inp{height:200px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> 
 

 
ID List: <Input id="txtBoxIds" type="text" value="[2, 3]"> 
 

 
<p>Input: 
 
<textarea id="inp">[ 
 
    { 
 
    "title": "category 1", 
 
    "children": [ 
 
     {"title": "subcategory 11", 
 
     "children": [ 
 
      {"id": 1, "title": "name 1"}, 
 
      {"id": 2, "title": "name 2"}, 
 
      {"id": 3, "title": "name 3"} 
 
     ] 
 
     }, 
 
     {"title": "subcategory 12", 
 
     "children": [ 
 
      {"id": 1, "title": "name 4"} 
 
     ] 
 
     } 
 
    ] 
 
    }, 
 
    { 
 
    "title": "category 2", 
 
    "children": [ 
 
     {"title": "subcategory 21", 
 
     "children": [ 
 
      {"id": 3, "title": "name cat2sub1id3"}, 
 
      {"id": 5, "title": "name cat2sub1id5"} 
 
     ] 
 
     }, 
 
     {"title": "subcategory 22", 
 
     "children": [ 
 
      {"id": 6, "title": "name cat2sub2id6"}, 
 
      {"id": 7, "title": "name cat2sub2id7"} 
 
     ] 
 
     } 
 
    ] 
 
    } 
 
]</textarea> 
 

 
<p>Pure-Javascript solution results: 
 
<textarea id="oupJS"></textarea> 
 

 
<p>jQuery solution results: 
 
<textarea id="oupJQ"></textarea>