给出以下JSON对象;
const data = [
{ "id": 1, "name": "Node 1", "children": [
{ "id": 3, "name": "Node 1.1", "children": [
{ "id": 6, "name": "Node 1.1.1", "children": [
{ "id": 12, "name": "Node 1.1.1.1", "children": [] }
]
},
{ "id": 7, "name": "Node 1.1.2", "children": [
{ "id": 13, "name": "Node 1.1.2.1", "children": [] }
]
}
]
},
{ "id": 4, "name": "Node 1.2", "children": [
{ "id": 8, "name": "Node 1.2.1", "children": [] },
{ "id": 9, "name": "Node 1.2.2", "children": [
{ "id": 14, "name": "Node 1.2.2.1", "children": [] },
{ "id": 15, "name": "Node 1.2.2.2", "children": [] }
]
}
]
}
]
},
{ "id": 2, "name": "Node 2", "children": [
{ "id": 5, "name": "Node 2.1", "children": [
{ "id": 10, "name": "Node 2.1.1", "children": [] },
{ "id": 11, "name": "Node 2.1.2", "children": [
{ "id": 16, "name": "Node 2.1.2.1", "children": [] }
]
}
]
}
]
}
];
我希望能够通过ID查找特定的节点,并且一旦找到该节点,便获得其直接父级和所有子级。例如,如果我想找到ID为9的节点(节点1.2.2),则希望它返回Node 1
,Node 1.2
,Node 1.2.2
及其子节点,并忽略其他所有内容。我已经部分使用此findById
函数;
findById(data, id)
{
let node = '';
data.some((currentNode) => {
return node = id === currentNode.id ? currentNode : this.findById(currentNode.children, id);
});
return node;
}
这样称呼;
this.data = [this.findById(this.data, id)];
但是它并不能完全满足我的要求。它找到正确的节点(因此在本例中为1.2.2)及其子节点(1.2.2.1和1.2.2.2),但找不到其直接父节点(1.2和1)。我该如何更改findById
函数以使其也包括直接父母?
所需的输出为;
const found = [
{ "id": 1, "name": "Node 1", "children": [
{ "id": 4, "name": "Node 1.2", "children": [
{ "id": 9, "name": "Node 1.2.2", "children": [
{ "id": 14, "name": "Node 1.2.2.1", "children": [] },
{ "id": 15, "name": "Node 1.2.2.2", "children": [] }
]
}
]
}
]
}
];
答案 0 :(得分:2)
您只需要存储递归函数的结果。为此,将您的三元分解成一个if,如下所示:
function findById(data, id) {
let node = null;
data.some((currentNode) => {
if (id === currentNode.id) {
return node = [currentNode];
}
const inItsTree = findById(currentNode.children, id);
if (inItsTree) {
return node = [{ ...currentNode, children: inItsTree }];
}
});
return node;
}
const data = [{"id":1,"name":"Node 1","children":[{"id":3,"name":"Node 1.1","children":[{"id":6,"name":"Node 1.1.1","children":[{"id":12,"name":"Node 1.1.1.1","children":[]}]},{"id":7,"name":"Node 1.1.2","children":[{"id":13,"name":"Node 1.1.2.1","children":[]}]}]},{"id":4,"name":"Node 1.2","children":[{"id":8,"name":"Node 1.2.1","children":[]},{"id":9,"name":"Node 1.2.2","children":[{"id":14,"name":"Node 1.2.2.1","children":[]},{"id":15,"name":"Node 1.2.2.2","children":[]}]}]}]},{"id":2,"name":"Node 2","children":[{"id":5,"name":"Node 2.1","children":[{"id":10,"name":"Node 2.1.1","children":[]},{"id":11,"name":"Node 2.1.2","children":[{"id":16,"name":"Node 2.1.2.1","children":[]}]}]}]}];
console.log(findById(data, 9));
答案 1 :(得分:0)
第一个问题是,这是正确的数据结构吗?您可以在链接列表中实现此功能吗?如果是这样,您可以对祖父母节点(父节点)有特定的引用。如果数据相对较小,则搜索时间是线性的,插入和删除是线性的。
class Node {
constructor(value){
this.value = value;
this.next = null;
this.children = [];
}
}
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
this.size = 0
}
insert(value) {
let node = new Node(value);
if(!this.head) {
this.head = node;
} else {
this.tail.next = node;
}
this.tail = node;
this.size++;
}
findNode(value) {
let node = this.head;
while(!!node) {
if(node.value === value){
return node;
}
node = node.next;
}
return `no node with ${value} found`
}
forEach(callback) {
let node = this.head;
while(!!node) {
callback(node.value);
node = node.next;
}
}
print() {
if(!this.head) {
return null
} else {
let node = this.head;
while(!!node){
console.log(node.value);
node = node.next
}
}
答案 2 :(得分:0)
将您的findById方法修改为:
function findById(data, id, parent)
{
let node = '';
data.some((currentNode) => {
if(id === currentNode.id) {
node = currentNode;
} else {
parent.push(currentNode); // track parent node
node = this.findById(currentNode.children, id, parent);
}
return node;
});
return {node: node, parent: parent};
}
this.data = [this.findById(this.data, 6, [])];
console.log(this.data[0].node); // will give you current node with children
console.log(this.data[0].parent); // will give you array of parents node
答案 3 :(得分:0)
递归是一种功能性遗产,因此这是使用功能性样式的解决方案-
// identity : 'a -> 'a
const identity = x =>
x
// findById : (number, node array) -> node?
const findById = (q = 0, [ n, ...more ], exit = identity) =>
n === undefined
? false
: findById1 (q, n, exit)
|| findById (q, more, exit)
// findById1 : (number, node) -> node?
const findById1 = (q = 0, n = {}, exit = identity) =>
n.id === q
? exit (n)
: findById (q, n.children, r =>
exit ({...n, children: [ r ] })
)
它是这样的-
findById (9, data)
// { id: 1, name: "Node 1", children: [
// { id: 4, name: "Node 1.2", children: [
// { id: 9, name: "Node 1.2.2", children: [
// { id: 14, name: "Node 1.2.2.1", children: [] },
// { id: 15, name: "Node 1.2.2.2", children: [] }
// ]}
// ]}
// ]}
找不到ID时,返回false
-
findById (99, data)
// false
展开以下代码段,以在您自己的浏览器中验证结果-
const identity = x =>
x
const findById = (q = 0, [ n, ...more ], exit = identity) =>
n === undefined
? false
: findById1 (q, n, exit)
|| findById (q, more, exit)
const findById1 = (q = 0, n = {}, exit = identity) =>
n.id === q
? exit (n)
: findById (q, n.children, r =>
exit ({...n, children: [ r ] })
)
const data =
[{id:1, name:"Node 1", children:[{id:3, name:"Node 1.1", children:[{id:6, name:"Node 1.1.1", children:[{id:12, name:"Node 1.1.1.1", children:[]}]},{id:7, name:"Node 1.1.2", children:[{id:13, name:"Node 1.1.2.1", children:[]}]}]},{id:4, name:"Node 1.2", children:[{id:8, name:"Node 1.2.1", children:[]},{id:9, name:"Node 1.2.2", children:[{id:14, name:"Node 1.2.2.1", children:[]},{id:15, name:"Node 1.2.2.2", children:[]}]}]}]},{id:2, name:"Node 2", children:[{id:5, name:"Node 2.1", children:[{id:10, name:"Node 2.1.1", children:[]},{id:11, name:"Node 2.1.2", children:[{id:16, name:"Node 2.1.2.1", children:[]}]}]}]}]
console.log (findById (9, data))
// { id: 1, name: "Node 1", children: [
// { id: 4, name: "Node 1.2", children: [
// { id: 9, name: "Node 1.2.2", children: [
// { id: 14, name: "Node 1.2.2.1", children: [] },
// { id: 15, name: "Node 1.2.2.2", children: [] }
// ]}
// ]}
// ]}
console.log (findById (99, data))
// false
连续传递样式泄漏了函数界面中的exit
参数。使用帮助程序功能,我们可以创建与您的原始API匹配的API-
// export findById : (number, node array) -> node?
const findById = (q = 0, nodes = []) =>
findAny (q, nodes, identity)
// private
const findAny = (q, [ node, ...more ], exit) =>
node === undefined
? false
: find1 (q, node, exit)
|| findAny (q, more, exit)
// private
const find1 = (q, node, exit) =>
node.id === q
? exit (node)
: findAny (q, node.children, r =>
exit ({...node, children: [ r ] })
)
最后,我们可以修改原始实现以创建更高阶的findBy
,而不是接受id
输入,它接受类似于Array.prototype.find
的功能-
// export findBy : ('node -> bool, node array) -> node?
const findBy = (f = identity, nodes = []) =>
findAny (f, nodes, identity)
// private
const findAny = (f, [ node, ...more ], exit) =>
node === undefined
? false
: find1 (f, node, exit)
|| findAny (f, more, exit)
// private
const find1 = (f, node, exit) =>
f (node)
? exit (node)
: findAny (f, node.children, r =>
exit ({...node, children: [ r ] })
)
通过专用findById
-
findByName
和findBy
// export findById : (number, node array) -> node?
const findById = (q = 0, nodes = []) =>
findBy (n => n.id === q, nodes, identity)
// export findByName : (string, node array) -> node?
const findByName = (q = "", nodes = []) =>
findBy (n => n.name === q, nodes, identity)
或者您可以通过任意东西找到-
findBy (node => node.id > 100 && node.name.length < 20, data)
// ...
希望这开始说明了功能样式的一些优点。