我的javascript程序中有类似于树结构的东西,我想从中移除一个子节点,我使用递归方法遍历树,但我很难从树中删除一个节点。
相反,我将获取一个ID作为参数,我应该针对child.metadata.id进行检查,如果它与ID匹配则删除子项。
注意:我的图表中有一些特定类型的子节点 它 路径基于其类型'true'或'false',如果是类型的 元数据中的子节点是if / empty。
以下是示例树结构。
var graph =
{
"metadata": {"id": "sms_in", "type": "api", "optionsDivId": "div_sms_in", "options": {}},
"data": {
"true": {
"isEmpty1": {
"metadata": {
"id": "isEmpty1",
"type": "empty",
"optionsDivId": "div_isEmpty1",
"options": {}
},
"data": {
"true": {
"sms1": {
"metadata": {
"id": "sms1",
"type": "api",
"optionsDivId": "div_sms1",
"options": {}
}, "data": {"true": {}, "false": false}
}
},
"false": {
"dbInsert1": {
"metadata": {
"id": "dbInsert1",
"type": "dbInsert",
"optionsDivId": "div_dbInsert1",
"options": {}
},
"data": {
"true": {
"sms2": {
"metadata": {
"id": "sms2",
"type": "api",
"optionsDivId": "div_sms2",
"options": {}
}, "data": {"true": {}, "false": false}
}
}, "false": false
}
}
}
}
}
}, "false": false
}
};
这是我的遍历函数
var traverse = function (current) {
if( current == 'undefined' ) return ;
var currentChildId = current['metadata']['id'];
var currentChildType = current['metadata']['type'];
console.log('visiting : ', currentChildId);
if (currentChildType == 'if' || currentChildType == 'empty') {
for(var childKeyType in current['data']){
for( var childKey in current['data'][childKeyType]){
var child = current['data'][childKeyType][childKey];
traverse(child);
}
}
} else {
for (var childKey in current['data']['true']) {
var child = current['data']['true'][childKey];
traverse(child);
}
}
};
有人可以帮我完成删除功能吗?
function popChild(current, childId){
if( current == 'undefined' ) return ;
var currentChildId = current['metadata']['id'];
var currentChildType = current['metadata']['type'];
if(currentChildId == childId){
delete current;
return;
}
console.log('visiting : ', currentChildId);
if (currentChildType == 'if' || currentChildType == 'empty') {
for(var childKeyType in current['data']){
for( var childKey in current['data'][childKeyType]){
var child = current['data'][childKeyType][childKey];
popChild(child, childId, );
}
}
} else {
for (var childKey in current['data']['true']) {
var child = current['data']['true'][childKey];
popChild(child, childId);
}
}
}
答案 0 :(得分:3)
使用JSON.stringify
作为迭代对象的方法,使用replacer
参数:
function remove_ids(object, id) {
return JSON.parse(JSON.stringify(object, function(key, value) {
if (typeof value !== 'object' || typeof value.metadata !== 'object' ||
value.metadata.id !== id) return value;
}));
}
答案 1 :(得分:0)
我会尽量不要在这里重新发明轮子。像这样,我们在大多数数据处理中都使用object-scan。一旦您将头放在头上,它就会很强大,并使代码更易于维护。这是解决问题的方法
const objectScan = require('object-scan');
const rm = (obj, id) => objectScan(['++.metadata.id'], {
abort: true,
rtn: 'bool',
filterFn: ({ value, parents, key }) => {
if (value === id) {
delete parents[2][key[key.length - 3]];
return true;
}
return false;
}
})(obj);
const graph = {"metadata":{"id":"sms_in","type":"api","optionsDivId":"div_sms_in","options":{}},"data":{"true":{"isEmpty1":{"metadata":{"id":"isEmpty1","type":"empty","optionsDivId":"div_isEmpty1","options":{}},"data":{"true":{"sms1":{"metadata":{"id":"sms1","type":"api","optionsDivId":"div_sms1","options":{}},"data":{"true":{},"false":false}}},"false":{"dbInsert1":{"metadata":{"id":"dbInsert1","type":"dbInsert","optionsDivId":"div_dbInsert1","options":{}},"data":{"true":{"sms2":{"metadata":{"id":"sms2","type":"api","optionsDivId":"div_sms2","options":{}},"data":{"true":{},"false":false}}},"false":false}}}}}},"false":false}};
console.log(rm(graph, 'isEmpty1'));
// => true
console.log(graph);
// => { metadata:
// { id: 'sms_in',
// type: 'api',
// optionsDivId: 'div_sms_in',
// options: {} },
// data: { true: {}, false: false } }
请注意,由于代码更改了现有结构,因此删除操作不适用于根节点。
答案 2 :(得分:0)
因为这已经复活了...
Vincent指出,经过战斗测试的库通常是解决此类问题的最佳方法。尽管我当然必须同意(并且实际上是Ramda的主要作者之一),但是还有另一种方法来维护自己的代码片段集合以在项目中重复使用。
尽管我从未真正考虑过将其包含在Ramda中,但我有一个方便的递归filter
函数,使编写此代码非常简单:
const filterDeep = (pred) => (obj) =>
Object (obj) === obj
? Object .fromEntries (
Object .entries (obj)
.flatMap (([k, v]) => pred (v) ? [[k, filterDeep (pred) (v)]] : [])
)
: obj
const removeItem = (targetId, graph) =>
filterDeep (({metadata: {id} = {}}) => id !== targetId) (graph)
const graph = {metadata: {id: "sms_in", type: "api", optionsDivId: "div_sms_in", options: {}}, data: {true: {isEmpty1: {metadata: {id: "isEmpty1", type: "empty", optionsDivId: "div_isEmpty1", options: {}}, data: {true: {sms1: {metadata: {id: "sms1", type: "api", optionsDivId: "div_sms1", options: {}}, data: {true: {}, false: !1}}}, false: {dbInsert1: {metadata: {id: "dbInsert1", type: "dbInsert", optionsDivId: "div_dbInsert1", options: {}}, data: {true: {sms2: {metadata: {id: "sms2", type: "api", optionsDivId: "div_sms2", options: {}}, data: {true: {}, false: !1}}}, false: !1}}}}}}, false: !1}}
console .log (removeItem ('dbInsert1', graph))
console .log (removeItem ('sms2', graph))
.as-console-wrapper {max-height: 100% !important; top: 0}
在这里,我们根据已经测试过的removeItem
编写了一个非常简单的filterDeep
函数。这是非常强大的。这样的功能通常不如流行库中的功能那么坚固。但是它们是直接为满足我们自己的需要而编写的,并且可以精确地设计为我们希望它们成为什么样子。
例如,将filter
与谓词一起使用时就有些奇怪了,但这与我们想要的相反。编写一个rejectDeep
可能会让我们的谓词测试匹配而不是不匹配。事实证明这很简单:
const rejectDeep = (pred) =>
filterDeep (x => ! (pred (x)))
const removeItem = (target, obj) =>
rejectDeep (({metadata: {id} = {}}) => id === target) (obj)
现在我们可以将rejectDeep
添加到我们的个人资料库中。
filterDeep
的一部分可能令人困惑。我们在这里将flatMap
用作一次通过的filter
和map
组合。将其分为两个步骤是完全合理的,但(可能)效率稍低:
const filterDeep = (pred) => (obj) =>
Object (obj) === obj
? Object .fromEntries (
Object .entries (obj)
.filter (([k, v]) => pred (v))
.map (([k, v]) => [k, filterDeep (pred) (v)])
)
: obj