0

CSV File Data:

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

Output: Javascript Object to be made

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

Basically, I have to convert a CSV file to make a Tree in d3. I am wondering how to make the dictionary required from the CSV file

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);
    });

So this is the code, I have which only make a dictionary-like, It doesn't go deeper than the 1st level of the root node

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

5
  • can you show where 'A3' is going to? Commented Nov 21, 2020 at 19:44
  • your example output seems wrong since there is no sign of A3 in the output. Commented Nov 21, 2020 at 19:44
  • please add you try. what goes wrong? Commented Nov 21, 2020 at 19:57
  • Updated what is wrong @NinaScholz Commented Nov 21, 2020 at 20:14
  • 1
    Why not use d3.stratify(). That's exactly what it's built for. Commented Nov 23, 2020 at 9:49

3 Answers 3

1

Currently you're only checking to see if the node matches the root (first) node. One idea might be to traverse the existing tree and check if the parent node you want to add already exists and then add a child to it. But this has some issues: it requires going back through the data, so it's inefficient; and what happens if you encounter a parent_node that isn't part of the tree yet? As vicatcu pointed out, a more efficient (and robust) way would be to create nodes as you encounter them and keep them in a dictionary/lookup object.

It could look something like this:

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`
});

Sign up to request clarification or add additional context in comments.

2 Comments

I have updated the data going in the program, doesn't seem to work on this data, Also @NinaScholz code is for an array of arrays but whenever we read data from a CSV file in d3 it is an array of dictionaries like yours
@Swaraj, I'm not sure what you mean. What do you expect the output to be? I've updated my code with the data in your question
1

You could take an object for keeping all relations and add unknown nodes as root nodes.

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; }

2 Comments

When you read a CSV file in d3 it is in the form of an array of dictionaries, not in the form of an array of arrays, I tried converting it but it doesn't work that way
I Have updated the dataset for input. Also, could you address the above comment @NinaScholz
0

First parse it using some library (I use csv-parse usually). Next, iterate over the resulting array and build out your final data structure progressively by adding each record to the tree, which of course involves determining for each row how the tree should be changed (be it adding a child or adding a sibling or whatever). One efficiency I would recommend is setting a dictionary of names to the objects iin your tree so as to avoid searching for them, like {id: noderef} so you can find out if the parent exists and where in the tree in constant time. For your reference, the csv format you have there is sometimes called an adjacency matrix in the literature.

2 Comments

I have uploaded my code, could you guide me where I am going wrong? @vicatcu
Hey @vicatcu could you please elaborate

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.