Guide on converting a hierarchical CSV file (using semicolon as delimiter) into a multidimensional array

My CSV file contains hierarchical data, with different levels separated by semicolons and values separated by commas. Here's an example:

Parent1;Child1;Grandchild1;3;3,5
Parent1;Child1;Grandchild2;3;3,5
Parent2;Child2;Grandchild2;4,4
Parent3;Child4;Grandchild1;5,5

I want to use javascript/jQuery/d3.js to transform this data into a multi-dimensional array like this:

var tree = [
  {
    text: "Parent 1",
    nodes: [
      {
        text: "Child 1",
        nodes: [
          {
            text: "Grandchild 1"
          },
          {
            text: "Grandchild 2"
          }
        ]
      },
      {
        text: "Child 2"
      }
    ]
  },
  {
    text: "Parent 2"
  },
  {
    text: "Parent 3"
  }
];

The challenge lies in recursively looping through the data and ensuring that the values do not become part of the multidimensional array.

Here is my current code, which is not functioning as intended:

function createNavi(jsonRoot){

function printParentAddChildren(parent){

    var parentObj = {};
    parentObj.text = parent.name; 
    parentObj.backColor = colors[parent.depth];

    var children = parent.children;
    if(children==null) return parentObj;

    parentObj.node = [];

    for(var i=0; i<children.length; i++){
        parentObj.node.push(printParentAddChildren(children[i]));    
    }
    return parentObj; 
}

    var tree = [];
    var children = jsonRoot.children;
    for(var i=0; i<children.length; i++){
        tree.push(printParentAddChildren(children[i]));    
    }
    $('#tree').treeview({
          data: tree
    });
    console.log(tree);   
};

Any assistance on this would be greatly appreciated. Thank you!

Answer №1

The d3.nest function is designed to handle tasks like this.

Assuming we have a file named test.csv with the following contents:

p;c;g;r1;r2
Parent1;Child1;Grandchild1;3;3,5
Parent1;Child1;Grandchild2;3;3,5
Parent2;Child2;Grandchild2;4,4
Parent3;Child4;Grandchild1;5,5

The following code snippet demonstrates how to use d3.nest:

var dsv = d3.dsv(";", "text/plain");
  dsv("test.csv", function(data){
    var n = d3.nest()
      .key(function(d){ return d.p })
      .key(function(d){ return d.c })
      .key(function(d){ return d.g })
      .entries(data);
  });

The result, stored in variable n, looks like this:

[{
  "key": "Parent1",
  "values": [{
    "key": "Child1",
    "values": [{
      "key": "Grandchild1",
      "values": [{
        "p": "Parent1",
        "c": "Child1",
        "g": "Grandchild1",
        "r1": "3",
        "r2": "3,5"
      }]
    }, {
      "key": "Grandchild2",
      "values": [{
        "p": "Parent1",
        "c": "Child1",
        "g": "Grandchild2",
        "r1": "3",
        "r2": "3,5"
      }]
    }]
  }]
}, {
  "key": "Parent2",
  "values": [{
    "key": "Child2",
    "values": [{
      "key": "Grandchild2",
      "values": [{
        "p": "Parent2",
        "c": "Child2",
        "g": "Grandchild2",
        "r1": "4,4"
      }]
    }]
  }]
}, {
  "key": "Parent3",
  "values": [{
    "key": "Child4",
    "values": [{
      "key": "Grandchild1",
      "values": [{
        "p": "Parent3",
        "c": "Child4",
        "g": "Grandchild1",
        "r1": "5,5"
      }]
    }]
  }]
}]

Link to the working code.


UPDATES BASED ON COMMENTS

  var dsv = d3.dsv(";", "text/plain");
  dsv("test.csv", function(data){
    var n = d3.nest()
      .key(function(d){ return d.p })
      .key(function(d){ return d.c })
      .rollup(function(d){
        return d.map(function(d1){
          return {key: d1.g};
        })
      })
      .entries(data);

    function renameKeys(obj){
      obj.forEach(function(d){
        d.text = d.key;
        delete d.key;
        if (d.values){
          d.nodes = d.values;
          delete d.values;
          renameKeys(d.nodes);
        }
      });
    }

    renameKeys(n);

  });

The resulting data structure is as follows:

[{
  "text": "Parent1",
  "nodes": [{
    "text": "Child1",
    "nodes": [{
      "text": "Grandchild1"
    }, {
      "text": "Grandchild2"
    }]
  }]
}, {
  "text": "Parent2",
  "nodes": [{
    "text": "Child2",
    "nodes": [{
      "text": "Grandchild2"
    }]
  }]
}, {
  "text": "Parent3",
  "nodes": [{
    "text": "Child4",
    "nodes": [{
      "text": "Grandchild1"
    }]
  }]
}]

Updated code.

Answer №2

It seems like we have reached the limit of what can be accomplished using pure JavaScript. Even though the code isn't lengthy, it is quite intricate. Let me present the code first and then walk through it.

var csv = "Parent1;Child1;Grandchild1;3;3,5\nParent1;Child1;Grandchild2;3;3,5\nParent2;Child2;Grandchild2;4,4\nParent3;Child4;Grandchild1;5,5";

function construct(csv){
  var arr = csv.split("\n").map(e => e.match(/[A-Za-z]+\d*/g));
  function nest(a,p){
    var fi = p.findIndex(e => e.text == a[0]);
    ~fi ? "nodes" in p[fi] ? nest(a.slice(1),p[fi].nodes)
                           : a.length > 1 && (p[fi].nodes = nest(a.slice(1),[]))
        : (p.push({"text": a[0]}),
           nest(a,p));
    return p;
  }
  return arr.reduce((p,c) => {p = nest(c,p); return p},[]);
}

document.write("<pre>" + JSON.stringify(construct(csv), null, 2) + "</pre>")

Alright, the construct function receives the csv text and starts by converting it into an array of arrays, with each word in a line becoming an item, like this:

[ [ 'Parent1', 'Child1', 'Grandchild1' ],
  [ 'Parent1', 'Child1', 'Grandchild2' ],
  [ 'Parent2', 'Child2', 'Grandchild2' ],
  [ 'Parent3', 'Child4', 'Grandchild1' ] ]

The construct function is responsible for building the main array containing the parent objects as items. It achieves this by using Array.prototype.reduce() with an initial empty array. The nest function handles inner array elements recursively and is where all the magic takes place.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Identifying instances where the AJAX success function exceeds a 5-second duration and automatically redirecting

Greetings! I have created a script that allows for seamless page transitions using Ajax without reloading the page. While the script functions perfectly, I am seeking to implement a feature that redirects to the requested page if the Ajax request takes lo ...

Why isn't offsetTop working for a div within a table in HTML and Javascript?

When using the offsetTop property to get the absolute position of an object, it works fine when the objects are outside of tables. However, if the object is inside a table, it always returns 1. Why does this happen and how can it be avoided? To see an exa ...

Leveraging ng-class with an Angular $scope attribute

My HTML structure includes: <div class="myDiv"> <div style="width:200px; height:200px;background-image:url('img/200x200/{{largeImg}}.png');" ng-class="{'magictime foolishIn': 1}"> <span> { ...

Retrieving Information from Website Database

My goal is to import data from a web query into Excel. However, I am facing a challenge with the IP address (e.g., 10.10.111.20) because it only displays page 1 with 20 rows of entry data. When I try to navigate to page 2 or beyond, the data does not updat ...

Preventing Duplicate Form Submissions in Rails 5 using jQuery

As a coding novice, I'm currently working on my Rails 5 app and implementing image cropping and uploading directly to AWS S3 from the client side using blueimp/jQuery-File-Upload. However, I have encountered an issue where multiple form submissions o ...

Error: The function 'stepUp' was invoked on an object lacking the HTMLInputElement interface during an AJAX request

It's always frustrating to have to ask a question that has already been asked, but I'm having trouble finding a solution that works for me. My issue involves retrieving the value of an input and sending it via AJAX. $("#cell_number").on("change" ...

Prevent Cordova from shrinking the view when focusing on an input

Currently working on developing an app using Cordova/Phonegap (v6.5.0). Platforms where I've installed the app: - IOS (issue identified) - Android (issue identified) - Browser (no issue found) I am facing a known problem without a suitabl ...

What are the best methods for utilizing scrollbars to navigate a virtual canvas?

I am interested in developing a unique jQuery plugin that can simulate a virtual HTML5 Canvas, where the canvas is not physically larger than its appearance on the page. However, the content intended for display on the canvas may be much larger and will ne ...

Three.js: Buffer geometry does not provide any performance improvement

After examining the Three.js example found at webgl_performance, I decided to try improving performance by converting the model into a buffer geometry using the following code: var buffer = THREE.BufferGeometryUtils.fromGeometry( geometry ); Despite my e ...

Encountering issues with formData in nextjs 13 due to incorrect data type

In my NextJS application, I am using the dataForm method to retrieve the values from a form's fields: export async function getDataForm(formData) { const bodyQuery = { ....... skip: formData.get("gridSkip") ...

Include quotation marks around a string in C# to convert it into JSON format

I am utilizing a service that operates with JSON format. However, the JSON data I am receiving does not include double quotes around keys and values. Here is an example of the data I have: [{name:{buyerfirstname:Randy, buyermiddlename:null, buyerlastnam ...

Textures have been added by the user in the three.js platform

Click here to access the jsFiddle adaptation of this problem. In my quest to develop a cutting-edge 3D web application, I aim to allow users to choose an image file from their device: <input id="userImage" type="file"/> Once a file is selected, th ...

What is the reason for the request body being undefined?

I have a JavaScript file named index.js that contains: const express = require('express'); const bodyParser = require('body-parser'); const cors = require('cors'); const db = require('./db'); const movieRouter = re ...

How to retrieve HTML attribute using D3 techniques

Looking to iterate through all rect nodes in the code snippet below: d3.selectAll("svg g rect") .on('mouseover', function (d) { console.log(this); }); When Console.log is executed, the following is printed: <rect class="cls" na ...

Looking to transfer data between pages using node.js and ejs for database access?

I am aiming to showcase the logged in username and quiz points on each page after the user logs in, and to increase the user's score when quiz answers are correct. I'm considering creating a straightforward JavaScript-based quiz, and then updati ...

Validating data for Telegram Web Bots using JavaScript

Struggling with creating a user verification script for my Telegram web app bots. Need help troubleshooting. import sha256 from 'js-sha256' const telegram = window.Telegram.WebApp const bot_token = '<bot-token>' const data_check_ ...

The original array remains unchanged, and the third scanf function is not allowing any inputs from me

Welcome to the event reservation program. I've encountered a couple of issues with the program and I'm struggling to find a solution. Currently, I've only written the program for case A and I need help resolving the following problems: #incl ...

Please provide instructions on how to update a webpage section using ajax/json

Currently, I am in the process of developing a chat website and focusing on managing ONLINE USERS. I have implemented AJAX to handle refreshing data, however, I am facing issues with using Append(); method. Whenever I refresh the section, the same data k ...

What are some ways to display unprocessed image data on a website using JavaScript?

I have an API endpoint that provides image files in raw data format. How can I display this image data on a website using the img tag or CSS background-image property, without utilizing canvas? One possible approach is shown below: $.get({ url: '/ ...

Unable to retrieve custom date picker value with React Antd

I have implemented a custom datepicker for entering dates in the 'MMDD' or 'MMDDYY' format. The date value is stored in state and used in the datepicker component as a controlled component. However, upon form submission, the datepicker ...