How not to skip the first element with .data() - d3.js

The data array contains exactly 5 objects.
The following code displays only the last 4, and skip the first :
d3.csv(csv_data, function(error, data){
var table = d3.select("#div");
var tr = table.selectAll("tr")
.data(data)
.enter()
.append("tr");
var td = tr.selectAll("td")
.data(function(d){return [d.x,d.y];})
.enter()
.append("td")
.html(function(d){return d;});
});

Related

Create a table structure in d3js from array of array and draw text or graph

I have an array of array where each subarray represents a row in my table.
Each row has: 5 text elements, and the last element is an object.
For each row I have to print the 5 texts in 5 cells, and in the 6th cell I have to draw a svg graph depending on the object characteristics.
I'm using d3js for this, and I'm getting stuck about how to discriminate when to print text and when to print the svg. Here's the code:
d3.select(".main")
.selectAll("div")
.data(newdata)
.enter()
.append("div")
.classed("row", true)
.selectAll(".row")
.data(function(d) { return d; })
.enter()
.append("div")
.classed("cell", true)
.append("span")
.classed("text", true)
.text(function(d) { return d; });
So for each row I create a div with class "row". Inside the row, I create a div for each cell with class "cell" and inside each of it I append a span with class "text" with the data text.
This works, but in the 6th cell I get of course the text representation of the object. In 6th cell of each row, I should not append span.text and print text but instead append a svg and begin to draw rectangles reading the object properties.
How should I do that?
Thanks a lot.
Create a selection of all the cells and then filter them based on the index in the row
var cells = d3.select(".main")
.selectAll("div")
.data(newdata)
.enter()
.append("div")
.classed("row", true)
.selectAll(".row")
.data(function(d) { return d; })
.enter()
.append("div")
.classed("cell", true);
cells.filter( (d,i) => i <= 4 )
.append("span")
.classed("text", true)
.text(function(d) { return d; });
cells.filter( (d,i) => i === 5 )
.append("svg");

path element showing in dom but not connected in svg

path element shows as individual points in the dom instead of one string of elements in SVG. The code looks like this
//------ BEGIN NEW CODE HERE ------
let univMenu = d3.select("#univDropdown");
univMenu
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){return d.key;})
.text(function(d){return d.key;})
// Function to create the initial graph
let initialGraph = function(univ){
let selectUniv = nest.filter(function(d){return d.key == univ;})
let selectUnivGroups = svg.selectAll(".univGroups")
.data(selectUniv, function(d){return d ? d.key : this.key;})
.enter()
.append("g")
.attr("class", "univGroups")
let initialPath = selectUnivGroups.selectAll(".line")
.data(function(d) { return d.values; })
.enter()
.append("path")
initialPath
.attr("d", function(d){return valueLine(d.values)})
.attr("class", "line")}
// Create initial graph
initialGraph("USHE Average")
// Update the data
let updateGraph = function(univ){
// Filter the data to include only fruit of interest
let selectUniv = nest.filter(function(d){return d.key == univ;})
// Select all of the grouped elements and update the data
let selectUnivGroups = svg.selectAll(".univGroups")
.data(selectUniv)
// Select all the lines and transition to new positions
selectUnivGroups.selectAll("path.line")
.data(function(d){return (d.values);})
.transition()
.duration(1000)
.attr("d", function(d){return valueLine(d.values)})}
// Run update function when dropdown selection changes
univMenu.on('change', function(){
let selectedUniv = d3.select(this)
.select("select")
.property("value")
updateGraph(selectedUniv)
});
The idea is I would have a selector to toggle between different universities to show their cost through the 'univMenu'.
Any help to get the path to show individually would be very greatly appreciated. I have the style in the CSS.
EDIT: the valueLine variable is defined as:
var valueLine = d3.line()
.x(function(d) { return x(d.Year); })
.y(function(d) { return y(+d.Currentdollars); });
A sample of the CSV is
University,USHE average,Constant dollars,Currentdollars,Year,% change from
previous year
University of Utah,NA,"$49,711 ",56990.00,2008,NA

Not understanding d3 v4 new general update pattern in nested data

I've used previous versions of D3 and coming back at v4 I've encountered I don't quite grasp the new update pattern. I have nested data and I wish to update my visualization with new child nodes, here is a minimum example:
function update(data) {
var rows = d3
.selectAll("div")
.data(data)
.enter()
.append("div")
.classed("row", true)
var cells = rows
.selectAll("div")
.data(function(d) { return d })
cells
.enter()
.append("div")
.classed("cell", true)
.text(function(d) {return d})
}
var arr = [
[1, 2, 3],
[1, 2, 3],
[1, 2, 3]
]
var button = d3.select("body")
.append("button")
.text("update")
.on("click", modifyData);
function modifyData(){
arr.forEach(row => {
row.push(4)
});
update(arr);
}
update(arr);
https://jsfiddle.net/59qnhb8d/
I would expect my viz to update with a column of 4's. Any hints appreciated.
You need to update and MERGE the new divs with the old divs:
function update(data) {
var rows = d3
.selectAll(".row")
.data(data)
rows.exit().remove();
var newRows = rows.enter()
.append("div")
.classed("row", true)
var cells = rows.merge(newRows) //here you tell it to merge new rows with existing rows
.selectAll(".cell")
.data(function(d) { console.log(d); return d })
cells.exit().remove();
var newCells = cells
.enter()
.append("div")
.classed("cell", true)
cells
.merge(newCells)
.text(function(d) {return d})
}
Fiddle here: https://jsfiddle.net/59qnhb8d/31/
A few things have likely changed here is Mike Bostocks General Update Pattern. Here is a fiddle with a working update Fiddle. There are 4 important steps in the update pattern:
1) Binding the new data
2) Removing nodes no longer needed
3) Merging non-removed nodes with the newly created ones
4) Updating nodes.
The change is in the update function, the updated version is below.
I went with the .each method for each row created to have better control over the nesting, and without having to do any modifications to the data object. The .each method uses the same pattern, just on its own nested nodes.
function update(data) {
var rows = d3.select("body").selectAll(".row").data(data); // bind data
rows.exit().remove(); // remove old nodes
rows.enter().append("div") // add new nodes
.classed("row", true)
.merge(rows) // merge new with existing and update all
.each(function(d){
let el = d3.select(this);
let cells = el.selectAll(".cell").data(d); // bind data
cells.exit().remove(); //remove old
cells.enter().append("div") //enter new
.classed("cell", true)
.merge(cells) //merge
.text(d=>d) // set text on all nodes.
});
}

How to pass subselection of data to child node?

If I have nested array like this
var ar = [[[1,0],[2,0],[3,0]], [[1,0],[2,0],[3,0]]]
I want to create two svg elements, this is easy
var svg = d3.select('div.main`)
.selectAll('svg')
.data(ar)
.enter()
.append('svg')
And now I want to bind subarrays to svg selection, something like this
var g = svg.selectAll('g')
.data(function(d,i) {return d[i];})
.enter()
.append('g')
after that the data attached to g should be
[[1,0],[2,0],[3,0]]
I know what this line is not correct .data(function(d,i) {return d[i];}) Just do not know how to explain it different way.
If I understand the question correctly,
Your are right, the issues arise from the identified line. You don't need to return d[i] as the data for the new selection, d represents each individual datum associated with each svg, d[i] represents only a one part of each datum.
If you want each datum, in its entirety, just append a g as normal:
var g = svg.append("g");
Try console.log on g.data() and you will see that your data is there still as you want, it is bound to each g.
You can then use each of these datums, bound to each g and carried over from each svg, as data to create new features. Passing the datum looks like: .data(function(d) { return d; }). The snippet below should help put it all together:
var data = [[[10,10],[30,30],[50,50]], [[10,20],[80,30],[50,60]] ];
var svg = d3.select('body')
.selectAll('svg')
.data(data)
.enter()
.append('svg')
.attr("height",100)
.attr("width",200);
var g = svg.append("g");
console.log("Data Bound To First G in First SVG:")
console.log(g.data()[0]);
console.log("Data Bound To Second G in Second SVG:")
console.log(g.data()[1]);
// Data is now available to make features:
g.selectAll("circle")
.data(function(d) { return d; })
.enter()
.append("circle")
.attr("r",10)
.attr("cx",function(d) { return d[0] })
.attr("cy",function(d) { return d[1] });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>

Animating table rows reording with d3.js

I want to reorder table's data and animate the table's row exchange smoothly.
Code can be found at http://jsfiddle.net/HCLe5/1/
var table = d3.select("body").select("table#d3table");
var tHead = table.append("thead");
tHead.append("tr");
tHead.selectAll("th").data(thead).enter().append("th").text(function(d) {return d.id});
var tBody = table.append("tbody");
var tr = tBody.selectAll("tr").data(tbody).enter().append("tr");
tr.selectAll("td")
.data(function(d){return d.row})
.enter()
.append("td")
.text(function(d){return d.value + d.id})
// Some magick row position manipulation

Resources