基本上我有一个像这样的JSON结构:
[
{
"id": "foo",
"parent_id": null,
"value": "This is a root node"
},
{
"id": "bar",
"parent_id": "foo",
"value": "This is 2nd level node"
},
{
"id": "baz",
"parent_id": "bar",
"value": "This is a 3rd level node"
},
{
"id": "moo",
"parent_id": "foo",
"value": "This is another 2nd level node"
},
{
"id": "woo",
"parent_id": null,
"value": "This is another root node"
}
]
(请注意,没有订购)。
现在我想把它解析成一个树形结构。像这样的东西(除了使用C ++类型,而不是JSON):
[
{
"id": "foo",
"value": "This is a root node",
"children": [
{
"id": "bar",
"value": "This is 2nd level node",
"children": [
{
"id": "baz",
"value": "This is a 3rd level node",
"children": []
}
]
},
{
"id": "moo",
"value": "This is 2nd level node"
"children": []
}
]
},
{
"id": "woo",
"value": "This is another root node"
"children": []
}
]
我会想象它看起来像这样,在伪代码中,但我不知道足够的C ++实际上使它工作(我正处于黑客周的中间。我不知道我是什么我在做:))
struct NodeValue {
std::string id;
std::string value;
std::string parent_id;
}
struct Node {
NodeValue value;
Node parent;
std::deque<Node> children;
}
std::deque<Node> buildTree(json::Value nodes) {
std::unordered_map<std::string, Node> lookup;
// Populate our lookup map with all the nodes
for (unsigned int i = 0; i < nodes.size(); ++i) {
lookup.insert(std::make_pair(nodes[i].id, Node {
NodeValue {
nodes[i]["id"].asString(),
nodes[i]["value"].asString(),
nodes[i]["parent_id"].asString()
},
nodes[i]["id"].asString()
}));
}
// Populate children
for (lookup::iterator i = lookup.begin(); i != lookup.end(); ++i) {
Node parent;
try {
parent = lookup.at(lookup[i].value.parent_id);
parent.children.emplace_back(lookup[i]);
} catch (out_of_range &e) {
continue;
}
}
// Remove all non-root nodes
it = lookup.begin();
while ((it = std::find_if(it, lookup.end(), [] (const Node& n) { return n.parent_id != NULL; })) != lookup.end()) {
lookup.erase(it++);
}
return lookup;
}
我不认为这个解决方案会起作用,除非节点根据其深度级别进行预先排序?另外,我两天前写了我的第一行C ++,所以请怜悯。
有人可以帮帮我吗?
答案 0 :(得分:1)
在Node
结构上使用方法以及特殊的“根节点”收集器:
struct Node {
NodeValue value;
Node* parent;
std::deque<Node> children;
void populateChildren(std::deque<Node>& source) {
while (source.size() > 0) {
std::deque<Node>::iterator child = std::find_if(
source.begin(), source.end(), [this](Node possibleChild) {
return possibleChild.value.parent_id == this->value.id;
}
);
if (source.end() == child) {
return;
}
child->populateChildren(source);
child->parent = this;
children.push_back(*child);
source.erase(child);
}
}
};
然后您的rootNode的ID为null
。然后,子项是parent_id
将匹配null
的顶级节点。
然后就这样做:
rootNode.populateChildren(setOfAllNodes);
我认为这应该有用,但我还没有测试过。
编辑:
得到它了 :)。以下是一个完整的工作示例(在GCC上)。基本思路与上面相同,但我不得不切换到指针,因为很明显我用erase
删除了底层对象。
#include <iostream>
#include <string>
#include <deque>
#include <algorithm>
struct NodeValue{
std::string id;
std::string name;
std::string parent_id;
};
struct Node {
NodeValue value;
Node* parent;
std::deque<Node*> children;
void populateChildren( std::deque<Node*>& source ) {
auto child = std::find_if( source.begin(), source.end(), [this](const Node* const node){
return node->value.parent_id == this->value.id;} );
while ( child != source.end() ){
(*child)->populateChildren( source );
(*child)->parent = this;
children.push_back( *child );
source.erase( child );
child = std::find_if( source.begin(), source.end(), [this](const Node* node){
return node->value.parent_id == this->value.id;
} );
}
}
};
void printChildCount( const Node* node ){
std::cout << "Node " << node->value.name << " has parent "
<< ( ( node->parent == NULL ) ? "null" : node->parent->value.name ) << " and children: "
<< getListString(node->children) << std::endl;
std::for_each( node->children.begin(), node->children.end(), printChildCount );
}
int main(){
std::deque<Node*> nodes;
Node node1{ NodeValue{ "1", "1", "root" }, NULL, std::deque<Node*>{} };
Node node2{ NodeValue{ "2", "2", "root" }, NULL, std::deque<Node*>{} };
Node node3{ NodeValue{ "3", "3", "1" }, NULL, std::deque<Node*>{} };
Node node4{ NodeValue{ "4", "4", "2" }, NULL, std::deque<Node*>{} };
Node node5{ NodeValue{ "5", "5", "2" }, NULL, std::deque<Node*>{} };
Node node6{ NodeValue{ "6", "6", "5" }, NULL, std::deque<Node*>{} };
Node node7{ NodeValue{ "7", "7", "5" }, NULL, std::deque<Node*>{} };
Node node8{ NodeValue{ "8", "8", "7" }, NULL, std::deque<Node*>{} };
nodes.push_back(&node5);
nodes.push_back(&node6);
nodes.push_back(&node3);
nodes.push_back(&node8);
nodes.push_back(&node4);
nodes.push_back(&node7);
nodes.push_back(&node1);
nodes.push_back(&node2);
Node rootNode { NodeValue { "root", "root", "" }, NULL, std::deque<Node*> { } };
rootNode.populateChildren( nodes );
printChildCount( &rootNode );
return 0;
}
答案 1 :(得分:0)
也许有点像这样的东西?
bool attachToParent(Node* node)
{
if (node->parent_id == id)
{
children.push_back(node);
return true;
}
for (Node* child : children)
{
if (child->attachToParent(node))
{
break;
}
}
return false;
}
然后你可以像这样使用它:
if (!root->attachToParent(node))
{
root->children.push_back(node);
}
答案 2 :(得分:0)
一位朋友帮我解决了这个问题:
struct NodeValue {
std::string id;
std::string name;
std::string parent_id;
};
struct Node {
NodeValue value;
Node* parent;
std::deque<Node> children;
void populateChildren(std::deque<Node>& source) {
for (Node &child : source) {
if (child.value.parent_id != value.id) {
continue;
}
children.push_back(child);
Node © = *(--children.end());
copy.populateChildren(source);
copy.parent = this;
}
}
};
这应该减少复制量,虽然源列表可能是不必要的迭代,但如果数据集很小,那么它肯定足够快。