3

I've created an html table from a JSON file, using d3.js. It shows values per country per year. It sort of works. The values for each country are floated to the left. So if there isn't a value for a particular year, the values for the other years move over into its place. (Take a look at the plunker as it makes a lot more sense once you see it-- the bottom table rows are where the issue is at.)

So my question is: how I can generate empty values / empty cells for these instances, so that the table reads correctly? Is this possible?

Code is below, and here is a plunker.

    <!DOCTYPE html>
    <html>
    <head>
        <style>
            body{
                font-family:Arial, sans-serif;
                font-size:14px;
            }
            table{
                border-spacing:0;
                padding:0;
            }
            #buildcontent{
                width:65%;
                float:left;
                margin-left:20px;
                margin-top:20px;
            }
            #buildcontent table#years{
                clear:both;
            }
            #buildcontent table#years td{
                width:30px;
                padding-left:5px;
                padding-right:35px;
            }
            #buildcontent table#countries{
                width:100%;
            }
            table#years td{
                border-top:1px solid #ddd;
              border-bottom:0px;
              font-weight:bold;
                padding-left:5px;
                padding-top:3px;
                height:18px
            }
            th{
              text-align:left;
              font-weight:normal !important;
              border-top:1px solid #ddd;
              border-left:1px solid #ddd;
              border-bottom:1px solid #ddd;
                height:25px;
                padding-left:5px;
                width: 200px;
            }
            td{
              border:1px solid #ddd;
                width:30px;
                height:25px;
                padding-left:5px;
            }
            tr.row-odd,
            .row-odd{
                background: #eee;
            }
        </style>
        <script src="http://d3js.org/d3.v3.min.js"></script>
        <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
        <div id="buildcontent">
          <table>
            <thead id="years">
            </thead>
            <tbody id="countries">
            </tbody>
          </table>
        </div>
    <script>

    d3.json("data.json", function(json) {
        json.forEach(function(d){
             d.value = Math.round((+d.value + 0.00001) * 1000) / 1000;
        });

                    var nestfilt = d3.nest()        
                            .key(function(d) { return d.country; })
                        .sortKeys(d3.ascending)
                            .map(json);

                  // add years 
                    var nestyr = d3.nest()      
                            .key(function(d) { return d.year; })
                            .sortKeys(d3.ascending)
                            .map(json);

                    var yearstring = Object.keys(nestyr);                               

                    var row = document.getElementById("years");
                    row.appendChild(document.createElement("td"));

                            yearstring.forEach(function (yr) {
                                var td = document.createElement("td");
                                td.textContent = yr;
                                row.appendChild(td);
                            });

                  var tr = d3.select("#countries")
                    .selectAll("tr")
                      .data(d3.entries(nestfilt));

                    tr.enter().append("tr");

                      // add stripes to the table
                    tr.attr("class", function(d, i){ if (i++ % 2 === 0){return 'row-even'}else {return 'row-odd'}});

                  var col1 = tr.append("th")
                      .html(function(d) { return d.key; })
                      .attr("id", "country");

                  var cells = tr.selectAll("td")
                      .data(function(d) { return d.value; });

                            cells.enter().append("td");
                            cells.text(function(d) { return d.value; });

                    tr.exit().remove();
                    cells.exit().remove();

    });

    </script>
    </body>
    </html>

Screenshot of rows with missing cells: Rows with missing cells

sprucegoose
  • 334
  • 5
  • 22
  • Use your inspector to check out what the structure is really like. You're not creating one table, you're actually creating two, one for the years and one for the countries. Try re-configuring your layout and you might find it a lot easier to tie it together – Will Ru Sep 21 '15 at 23:48
  • Hi. True, there were two tables. The separate table that held the years was not really a problem. Nevertheless, I combined to one table. It's cleaner and better, so thanks. Still, my original issue remains exactly the same. I've attached a screenshot of the problematic rows to further clarify. – sprucegoose Sep 22 '15 at 01:04

2 Answers2

1

A simple thing you can do is to make a small function to see if cell's content is empty or not, and if it's empty (value does not exist for the given year), make the content of the cell "not available" or " " instead of leaving it empty. Hope it helps. :)

Arham
  • 51
  • 1
  • I had tied the data to the creation of the table cells (a cell was only created _if_ there was data.), so there were no cells to check against. – sprucegoose Sep 26 '15 at 22:07
0

I needed to take a different approach to creating the table. This stackoverflow post was extremely helpful. Then I restructured my JSON to fit, so that there were not separate object properties for "year" and "value" but that they were married-- where the former value for year became the key. So for instance, this:

    {
      "year": 1980,
      "value": 0.23
    }

became this:

    {
      "1980": 0.23
    }

Here's a new plunker that shows the code working correctly, and the full code is also below:

    <!DOCTYPE html>
    <html>
    <head>
        <style>
            body{
                font-family:Arial, sans-serif;
                font-size:14px;
            }
            table{
                border-spacing:0;
                padding:0;
            }
            #buildcontent table#years td{
                width:20px;
                padding-left:5px;
                padding-right:5px;
            }
            table#years td{
                border-top:1px solid #ddd;
              border-bottom:0px;
              font-weight:bold;
                padding-left:5px;
                padding-top:3px;
                height:18px;
            }
            th{
              text-align:left;
              font-weight:normal !important;
              border-top:1px solid #ddd;
              border-left:1px solid #ddd;
              border-bottom:1px solid #ddd;
                height:25px;
                padding-left:5px;
                width: 50px;
                font-weight:bold !important;
            }
            td{
              border:1px solid #ddd;
                width:20px;
                height:25px;
                padding-left:5px;
            }
            tr.row-odd,
            .row-odd{
                background: #eee;
            }
        </style>
        <script src="http://d3js.org/d3.v3.min.js"></script>
        <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
        <div id="buildcontent">
          <table>
            <thead id="years">
            </thead>
            <tbody id="countries">
            </tbody>
          </table>
        </div>
    <script>


    d3.json("data.json", function(json) {
        json.forEach(function(d){
             d.value = Math.round((+d.value + 0.00001) * 1000) / 1000;
        });

        var newData = [],
            countries = {};
        json.forEach(function (d) {
            var country = countries[d.country];
            if (!country) {
                newData.push(country = countries[d.country] = {});
            }
             country[d.year] = d.value,
               countries[d.country].Country = d.country;
        });

                  // add years 
                    var nestyr = d3.nest()      
                            .key(function(d) { return d.year; })
                            .sortKeys(d3.ascending)
                            .map(json);

                    var yearstring = Object.keys(nestyr);                               
                    yearstring.unshift("Country");


                  function tabulate(newData, columns) {
                        var table = d3.select('body').append('table')
                        var thead = table.append('thead')
                        var tbody = table.append('tbody');

                        // append the header row
                        thead.append('tr')
                          .selectAll('th')
                          .data(columns).enter()
                          .append('th')
                            .text(function (column) { return column; });

                        // create a row for each object in the data
                        var rows = tbody.selectAll('tr')
                          .data(newData)
                          .enter()
                          .append('tr');

                          // add stripes to the table
                        rows.attr("class", function(d, i){ if (i++ % 2 === 0){return 'row-even'}else {return 'row-odd'}});

                        // create a cell in each row for each column
                        var cells = rows.selectAll('td')
                          .data(function (row) {
                            return columns.map(function (column) {
                              return {column: column, value: row[column]};
                            });                 
                          })
                          .enter()
                          .append('td')
                                .attr("class", function(d){ return d.year; })
                            .text(function (d) { return d.value; });

                      return table;
                    }

                        // render the table(s)
                        tabulate(newData, yearstring);

    });

    </script>
    </body>
    </html>
Community
  • 1
  • 1
sprucegoose
  • 334
  • 5
  • 22