0

CSV 文件数据:

parent_name, child_name
A1, A2
A1, A3
A1, A4
A1, A5
A2, A12
A2, A16
A2, A18
A2, A19

输出:要制作的 Javascript 对象

{
name: A1,
children: [
            {
             name: A2,
             children: [
                        {
                         name: A4,
                         children: []
                        }
                       ]
            },
            {
             name: A3,
             children: []
            }
}

基本上,我必须转换一个 CSV 文件才能在 d3 中创建一个树。我想知道如何制作 CSV 文件所需的字典

d3.csv("test1.csv").then(function (data) {
        var dataset = {};
        data.forEach(function (d,i){
            console.log(d.parent_name, d.child_name);
            if(i === 0){
                dataset["name"]  = d.parent_name;
                dataset["children"] = [];
            }
            if (dataset["name"] === d.parent_name){
                dataset.children.push(NodeMaker(d.child_name))
            }
        });
        function NodeMaker(name){
            return {"name": name, "children": []};
        }
        console.log(dataset);
    });

所以这是代码,我只有一个类似字典的代码,它不会比根节点的第一级更深

{
name: A1,
children: [
           {
             name: A2,
             children: []
           },
           {
             name: A3,
             children: []
           }
           ]
}

4

3 回答 3

1

您可以使用一个对象来保留所有关系并将未知节点添加为根节点。

const
    data = [['A1', 'A2'], ['A1', 'A3'], ['A2', 'A4']],
    tree = function (data) {
        const t = { root: { children: [] } };
        data.forEach(([parent, name]) => {
            t[name] = t[name] || { name, children: [] };
            if (!t[parent]) t.root.children.push(t[parent] = { name: parent, children: [] });
            t[parent].children.push(t[name]);
         
        });
        return t.root.children;
    }(data);

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

于 2020-11-21T20:30:34.413 回答
1

目前,您只检查节点是否与根(第一个)节点匹配。一种想法可能是遍历现有树并检查要添加的父节点是否已经存在,然后向其添加子节点。但这有一些问题:它需要回溯数据,因此效率低下;如果你遇到parent_node还不是树的一部分会发生什么?正如vicatcu 指出的那样,一种更有效(且更健壮)的方法是在遇到节点时创建节点并将它们保存在字典/查找对象中。

它可能看起来像这样:

var sampleData = [
    { parent_name: 'A1', child_name: 'A2' },
    { parent_name: 'A1', child_name: 'A3' },
    { parent_name: 'A1', child_name: 'A4' },
    { parent_name: 'A1', child_name: 'A5' },
    { parent_name: 'A2', child_name: 'A12' },
    { parent_name: 'A2', child_name: 'A16' },
    { parent_name: 'A2', child_name: 'A18' },
    { parent_name: 'A2', child_name: 'A19' }
]

// if loading from a file, use this line instead to load data
// d3.csv("test1.csv").then(function (data) {
Promise.resolve(sampleData).then(function (data) {
    var lookup = {};
    data.forEach(function (d,i) {
        var parentNode = getNode(d.parent_name);
        parentNode.children.push(getNode(d.child_name));
    });
    function getNode(name) {
        if (!lookup[name]) {
            // if the node doesn't exist, make it
            lookup[name] = NodeMaker(name);
        }
        return lookup[name];
    }
    function NodeMaker(name){
        return {"name": name, "children": []};
    }
    // if the first parent node is the root, it represents the whole tree
    var tree = lookup[data[0].parent_name];
    console.log(tree);
    // do something with `tree`
});

于 2020-11-21T20:36:45.647 回答
0

首先使用一些库解析它(我通常使用 csv-parse)。接下来,遍历结果数组并通过将每条记录添加到树中逐步构建最终数据结构,这当然涉及为每一行确定树应如何更改(添加子节点或添加兄弟节点或其他) . 我建议的一种效率是为树中的对象设置名称字典,以避免搜索它们,例如 {id: noderef} 以便您可以在恒定时间内找出父对象是否存在以及树中的位置。供您参考,您拥有的 csv 格式在文献中有时称为邻接矩阵。

于 2020-11-21T19:43:30.793 回答