我有一组深度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”的对象。
问题是这些对象的深度是可变的和未知的 - 有些对象没有子节点,有些子节点也有子节点等等,任何子类别本身都可以有子类别,我基本上需要找不到对象已定义ID并保留每个ID的完整路径的子项。
谢谢。
答案 0 :(得分: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);
答案 1 :(得分:0)
您可以对deepdash
使用lodash
扩展名中的filterDeep方法:
var obj = [{/* get Vijay Jagdale's source object as example */}];
var idList = [2, 3];
var found = _.filterDeep(
obj,
function(value) {
return _.indexOf(idList, value.id) !== -1;
},
{ tree: true }
);
filtrate
对象将是:
[ { title: 'category 1',
children:
[ { title: 'subcategory 11',
children:
[ { id: 2, title: 'name 2' },
{ id: 3, title: 'name 3' } ] } ] },
{ title: 'category 2',
children:
[ { title: 'subcategory 21',
children: [ { id: 3, title: 'name cat2sub1id3' } ] } ] } ]
这是您的用例的full working test
答案 2 :(得分:0)
我不会重新发明轮子。现在,我们在大多数数据处理中使用object-scan,它可以很好地解决您的问题。这是
const objectScan = require('object-scan');
const filter = (input, ids) => {
objectScan(['**[*]'], {
filterFn: ({ value, parent, property }) => {
if (
('id' in value && !ids.includes(value.id))
|| ('children' in value && value.children.length === 0)
) {
parent.splice(property, 1);
}
}
})(input);
};
const 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":[]}];
filter(hierarchy, [2, 3]);
console.log(JSON.stringify(hierarchy));
// => [{"title":"category 1","children":[{"title":"subcategory 1","children":[{"id":2,"title":"name 2"},{"id":3,"title":"name 3"}]}]}]