Unable to merge selectionsHow can I merge properties of two JavaScript objects dynamically?How can I know which radio button is selected via jQuery?How can I select an element with multiple classes in jQuery?Get selected value in dropdown list using JavaScriptHow to merge two arrays in JavaScript and de-duplicate itemsGet selected text from a drop-down list (select box) using jQueryCan enter() selection be reused after append/insert?Merge/flatten an array of arraysX & Y Co-ordinates of selective bars in a stack graphMerge selection with groups
When you have to wait for a short time
Is this statement about a motion being simple harmonic in nature strong?
Welche normative Autorität hat der Duden? / What's the normative authority of the Duden?
Can authors email you PDFs of their textbook for free?
What are ways to record who took the pictures if a camera is used by multiple people?
Do universities maintain secret textbooks?
A word for the urge to do the opposite
What is the following VRP?
Turn off Google Chrome's Notification for "Flash Player will no longer be supported after December 2020."
Can two aircraft be allowed to stay on the same runway at the same time?
apt-file regex: find multiple packages at once using or
How would a disabled person earn their living in a medieval-type town?
Could a complex system of reaction wheels be used to propel a spacecraft?
LINQ Extension methods MinBy and MaxBy
Can I leave a large suitcase at TPE during a 4-hour layover, and pick it up 4.5 days later when I come back to TPE on my way to Taipei downtown?
Can you use Apple Care+ without any checks (bringing just MacBook)?
Doubt about a particular point of view on how to do character creation
How can I portray a character with no fear of death, without them sounding utterly bored?
Does using composite keys violate 2NF
How does the search space affect the speed of an ILP solver?
Longer digital trace further from analog vs shorter digital trace closer to analog
Modeling an M1A2 Smoke Grenade Launcher
Ideas behind the 8.Bd3 line in the 4.Ng5 Two Knights Defense
Did NASA/JPL get "waning" and "waxing" backwards in this video?
Unable to merge selections
How can I merge properties of two JavaScript objects dynamically?How can I know which radio button is selected via jQuery?How can I select an element with multiple classes in jQuery?Get selected value in dropdown list using JavaScriptHow to merge two arrays in JavaScript and de-duplicate itemsGet selected text from a drop-down list (select box) using jQueryCan enter() selection be reused after append/insert?Merge/flatten an array of arraysX & Y Co-ordinates of selective bars in a stack graphMerge selection with groups
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
Firstly, I'm brand new to D3, but searching has yielded no results that work for my case.
I'm using latest D3 v5.
I'm trying to get d3 to fill in a table with a single column. I'm able to accomplish this in a manner that violates DRY:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
TRS.enter()
.append("tr")
.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td")
.text(function fillTD(d)return d.name;);
TRS
.selectAll("td")
.data(function(row)return [row];)
.text(function fillTD(d)
return d.name;
);
As you can see, I'm copy-pasting the function "fillTD".
Googling led me to the merge function, then down a rabbit hole of trying to get it to work:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
Merged
.text(function fillTD(d)return d.name;);
In this, the TRs and TDs are generated just fine. The battle is solely over the functions after merge is called.
The values of the variable passed as an argument to merge never show. That is, when I have TDsEntered.merge(TDS)
, only TDS's selections are passed, as if the merge never occurred and only new items are created. This behavior persists with TDS.merge(TDsEntered), though now only updates occur.
If I remove the .data line from here:
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
It works just the same.
If I swap RowsEntered's use for TDsEntered:
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = TRS.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
I get the same behavior, but only after a second pass through the fromEvent that fires this. Which makes sense.
When inspected in the debugger, TDS and TDsEntered have variable numbers of "groups" and "parents", depending on if they're being used. E.g., TDsEntered will have 4 groups & parents that correspond to 4 new entries. If the are no changes to any existing entries, TDS will, in turn, have 0 groups & parents.
This is not the case if I attempt to merge TRS and RowsEntered. Both of them have equal number of parents & groups, and the merged result has the same value.
So, I thought I could use TRS and RowsEntered:
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.text(function fillTD(d)return d.name;);
But now only new entries work. I flipped TRS and RowsEntered's places, but no change: still only new entries.
Okay.
So I violate DRY again by copying that same data selector used when TDsEntered was made:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td");
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.data(function(row)return [row];)
.text(function fillTD(d)return d.name;);
And this WORKS.
But I'm violating DRY again, just less.
So I try making a "TDSReselected" with that formula to prevent DRY:
var TDSReselected = RowsEntered.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDSReselected
.enter()
.append("td");
var Merge1 = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
var Merge3 = TDsEntered.merge(TDSReselected);
Merge3
.text(function fillTD(d)return d.name;);
But nope, doesn't work. Back to the same issue.
What gives?
Is there any way to tackle this without having my data() logic in two places? What am I doing wrong?
EDIT AFTER GERARDO'S EXPLANATION:
This works now. The key that Gerardo brought to light was that the rows needed merging first, and then the TDs' update&enter are built off the merged rows:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var RowsEntered = TRS.enter()
.append("tr")
var RowsMerged = TRS.merge(RowsEntered);
var TDS = RowsMerged.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDS
.enter()
.append("td");
var TDMerged = TDS.merge(TDsEntered);
TDMerged
.text(function fillTD(d)return d.name;);
This made everything work, and we no longer repeat any logic!
Note: I don't exit the TDs because in my specific case (TD count for a statically defined column count table will remain static), I don't need to.
If my count of columns was dynamic, I would need to exit the TDs.
javascript d3.js
add a comment |
Firstly, I'm brand new to D3, but searching has yielded no results that work for my case.
I'm using latest D3 v5.
I'm trying to get d3 to fill in a table with a single column. I'm able to accomplish this in a manner that violates DRY:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
TRS.enter()
.append("tr")
.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td")
.text(function fillTD(d)return d.name;);
TRS
.selectAll("td")
.data(function(row)return [row];)
.text(function fillTD(d)
return d.name;
);
As you can see, I'm copy-pasting the function "fillTD".
Googling led me to the merge function, then down a rabbit hole of trying to get it to work:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
Merged
.text(function fillTD(d)return d.name;);
In this, the TRs and TDs are generated just fine. The battle is solely over the functions after merge is called.
The values of the variable passed as an argument to merge never show. That is, when I have TDsEntered.merge(TDS)
, only TDS's selections are passed, as if the merge never occurred and only new items are created. This behavior persists with TDS.merge(TDsEntered), though now only updates occur.
If I remove the .data line from here:
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
It works just the same.
If I swap RowsEntered's use for TDsEntered:
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = TRS.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
I get the same behavior, but only after a second pass through the fromEvent that fires this. Which makes sense.
When inspected in the debugger, TDS and TDsEntered have variable numbers of "groups" and "parents", depending on if they're being used. E.g., TDsEntered will have 4 groups & parents that correspond to 4 new entries. If the are no changes to any existing entries, TDS will, in turn, have 0 groups & parents.
This is not the case if I attempt to merge TRS and RowsEntered. Both of them have equal number of parents & groups, and the merged result has the same value.
So, I thought I could use TRS and RowsEntered:
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.text(function fillTD(d)return d.name;);
But now only new entries work. I flipped TRS and RowsEntered's places, but no change: still only new entries.
Okay.
So I violate DRY again by copying that same data selector used when TDsEntered was made:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td");
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.data(function(row)return [row];)
.text(function fillTD(d)return d.name;);
And this WORKS.
But I'm violating DRY again, just less.
So I try making a "TDSReselected" with that formula to prevent DRY:
var TDSReselected = RowsEntered.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDSReselected
.enter()
.append("td");
var Merge1 = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
var Merge3 = TDsEntered.merge(TDSReselected);
Merge3
.text(function fillTD(d)return d.name;);
But nope, doesn't work. Back to the same issue.
What gives?
Is there any way to tackle this without having my data() logic in two places? What am I doing wrong?
EDIT AFTER GERARDO'S EXPLANATION:
This works now. The key that Gerardo brought to light was that the rows needed merging first, and then the TDs' update&enter are built off the merged rows:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var RowsEntered = TRS.enter()
.append("tr")
var RowsMerged = TRS.merge(RowsEntered);
var TDS = RowsMerged.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDS
.enter()
.append("td");
var TDMerged = TDS.merge(TDsEntered);
TDMerged
.text(function fillTD(d)return d.name;);
This made everything work, and we no longer repeat any logic!
Note: I don't exit the TDs because in my specific case (TD count for a statically defined column count table will remain static), I don't need to.
If my count of columns was dynamic, I would need to exit the TDs.
javascript d3.js
add a comment |
Firstly, I'm brand new to D3, but searching has yielded no results that work for my case.
I'm using latest D3 v5.
I'm trying to get d3 to fill in a table with a single column. I'm able to accomplish this in a manner that violates DRY:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
TRS.enter()
.append("tr")
.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td")
.text(function fillTD(d)return d.name;);
TRS
.selectAll("td")
.data(function(row)return [row];)
.text(function fillTD(d)
return d.name;
);
As you can see, I'm copy-pasting the function "fillTD".
Googling led me to the merge function, then down a rabbit hole of trying to get it to work:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
Merged
.text(function fillTD(d)return d.name;);
In this, the TRs and TDs are generated just fine. The battle is solely over the functions after merge is called.
The values of the variable passed as an argument to merge never show. That is, when I have TDsEntered.merge(TDS)
, only TDS's selections are passed, as if the merge never occurred and only new items are created. This behavior persists with TDS.merge(TDsEntered), though now only updates occur.
If I remove the .data line from here:
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
It works just the same.
If I swap RowsEntered's use for TDsEntered:
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = TRS.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
I get the same behavior, but only after a second pass through the fromEvent that fires this. Which makes sense.
When inspected in the debugger, TDS and TDsEntered have variable numbers of "groups" and "parents", depending on if they're being used. E.g., TDsEntered will have 4 groups & parents that correspond to 4 new entries. If the are no changes to any existing entries, TDS will, in turn, have 0 groups & parents.
This is not the case if I attempt to merge TRS and RowsEntered. Both of them have equal number of parents & groups, and the merged result has the same value.
So, I thought I could use TRS and RowsEntered:
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.text(function fillTD(d)return d.name;);
But now only new entries work. I flipped TRS and RowsEntered's places, but no change: still only new entries.
Okay.
So I violate DRY again by copying that same data selector used when TDsEntered was made:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td");
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.data(function(row)return [row];)
.text(function fillTD(d)return d.name;);
And this WORKS.
But I'm violating DRY again, just less.
So I try making a "TDSReselected" with that formula to prevent DRY:
var TDSReselected = RowsEntered.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDSReselected
.enter()
.append("td");
var Merge1 = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
var Merge3 = TDsEntered.merge(TDSReselected);
Merge3
.text(function fillTD(d)return d.name;);
But nope, doesn't work. Back to the same issue.
What gives?
Is there any way to tackle this without having my data() logic in two places? What am I doing wrong?
EDIT AFTER GERARDO'S EXPLANATION:
This works now. The key that Gerardo brought to light was that the rows needed merging first, and then the TDs' update&enter are built off the merged rows:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var RowsEntered = TRS.enter()
.append("tr")
var RowsMerged = TRS.merge(RowsEntered);
var TDS = RowsMerged.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDS
.enter()
.append("td");
var TDMerged = TDS.merge(TDsEntered);
TDMerged
.text(function fillTD(d)return d.name;);
This made everything work, and we no longer repeat any logic!
Note: I don't exit the TDs because in my specific case (TD count for a statically defined column count table will remain static), I don't need to.
If my count of columns was dynamic, I would need to exit the TDs.
javascript d3.js
Firstly, I'm brand new to D3, but searching has yielded no results that work for my case.
I'm using latest D3 v5.
I'm trying to get d3 to fill in a table with a single column. I'm able to accomplish this in a manner that violates DRY:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
TRS.enter()
.append("tr")
.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td")
.text(function fillTD(d)return d.name;);
TRS
.selectAll("td")
.data(function(row)return [row];)
.text(function fillTD(d)
return d.name;
);
As you can see, I'm copy-pasting the function "fillTD".
Googling led me to the merge function, then down a rabbit hole of trying to get it to work:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
Merged
.text(function fillTD(d)return d.name;);
In this, the TRs and TDs are generated just fine. The battle is solely over the functions after merge is called.
The values of the variable passed as an argument to merge never show. That is, when I have TDsEntered.merge(TDS)
, only TDS's selections are passed, as if the merge never occurred and only new items are created. This behavior persists with TDS.merge(TDsEntered), though now only updates occur.
If I remove the .data line from here:
var TDS = TRS.selectAll("td")
.data(function(row)return [row];);
It works just the same.
If I swap RowsEntered's use for TDsEntered:
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = TRS.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
I get the same behavior, but only after a second pass through the fromEvent that fires this. Which makes sense.
When inspected in the debugger, TDS and TDsEntered have variable numbers of "groups" and "parents", depending on if they're being used. E.g., TDsEntered will have 4 groups & parents that correspond to 4 new entries. If the are no changes to any existing entries, TDS will, in turn, have 0 groups & parents.
This is not the case if I attempt to merge TRS and RowsEntered. Both of them have equal number of parents & groups, and the merged result has the same value.
So, I thought I could use TRS and RowsEntered:
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.text(function fillTD(d)return d.name;);
But now only new entries work. I flipped TRS and RowsEntered's places, but no change: still only new entries.
Okay.
So I violate DRY again by copying that same data selector used when TDsEntered was made:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var TDS = TRS.selectAll("td");
var RowsEntered = TRS.enter()
.append("tr")
var TDsEntered = RowsEntered.selectAll("td")
.data(function(row)return [row];)
.enter()
.append("td");
var Merged = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
Merge2
.selectAll('td')
.data(function(row)return [row];)
.text(function fillTD(d)return d.name;);
And this WORKS.
But I'm violating DRY again, just less.
So I try making a "TDSReselected" with that formula to prevent DRY:
var TDSReselected = RowsEntered.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDSReselected
.enter()
.append("td");
var Merge1 = TDS.merge(TDsEntered);
var Merge2 = TRS.merge(RowsEntered);
var Merge3 = TDsEntered.merge(TDSReselected);
Merge3
.text(function fillTD(d)return d.name;);
But nope, doesn't work. Back to the same issue.
What gives?
Is there any way to tackle this without having my data() logic in two places? What am I doing wrong?
EDIT AFTER GERARDO'S EXPLANATION:
This works now. The key that Gerardo brought to light was that the rows needed merging first, and then the TDs' update&enter are built off the merged rows:
var TRS = d3.select("#queueTable tbody")
.selectAll("tr")
.data(data);
TRS.exit().remove();
var RowsEntered = TRS.enter()
.append("tr")
var RowsMerged = TRS.merge(RowsEntered);
var TDS = RowsMerged.selectAll("td")
.data(function(row)return [row];);
var TDsEntered = TDS
.enter()
.append("td");
var TDMerged = TDS.merge(TDsEntered);
TDMerged
.text(function fillTD(d)return d.name;);
This made everything work, and we no longer repeat any logic!
Note: I don't exit the TDs because in my specific case (TD count for a statically defined column count table will remain static), I don't need to.
If my count of columns was dynamic, I would need to exit the TDs.
javascript d3.js
javascript d3.js
edited Mar 28 at 13:58
Patrick Mascari
asked Mar 27 at 23:55
Patrick MascariPatrick Mascari
83 bronze badges
83 bronze badges
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
First of all, according to the very creator of the DRY principle, DRY is not exactly the same of just avoiding repetition of code:
“Most people take DRY to mean you shouldn't duplicate code. That's not its intention. The idea behind DRY is far grander than that” (source)
In D3 you'll find yourself repeating some snippets again and again. That's not a problem per se, as long as you know what you're doing.
Back to your question:
You can simplify your code and avoid creating a mess if you know clearly what each selection is doing. Better than just saying each selection, we should say each update pattern.
In your case, you have two elements that should be created, updated and removed: <tr>
and <td>
.
So, you can write a very clean pattern for tr
s:
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
And another one for the td
s:
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
Since you're appending just one <td>
in each <tr>
(single-column), the last pattern can be greatly simplified. But let's keep it that way for now.
Also, don't forget to give meaningful names for the selections:
const body = d3.select("body");
const table = body.append("table");
Here is a demo:
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
Regarding your main question: "what am I doing wrong?" Many things, depending on the snippet. For instance, sometimes you're merging an enter selection based on an outer selection which was not merged... A comprehensive answer would be too long. Just have a look at my snippet and see that we create the update pattern for the outer level, and then the update pattern for the inner level, and so on...
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55388212%2funable-to-merge-selections%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
First of all, according to the very creator of the DRY principle, DRY is not exactly the same of just avoiding repetition of code:
“Most people take DRY to mean you shouldn't duplicate code. That's not its intention. The idea behind DRY is far grander than that” (source)
In D3 you'll find yourself repeating some snippets again and again. That's not a problem per se, as long as you know what you're doing.
Back to your question:
You can simplify your code and avoid creating a mess if you know clearly what each selection is doing. Better than just saying each selection, we should say each update pattern.
In your case, you have two elements that should be created, updated and removed: <tr>
and <td>
.
So, you can write a very clean pattern for tr
s:
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
And another one for the td
s:
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
Since you're appending just one <td>
in each <tr>
(single-column), the last pattern can be greatly simplified. But let's keep it that way for now.
Also, don't forget to give meaningful names for the selections:
const body = d3.select("body");
const table = body.append("table");
Here is a demo:
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
Regarding your main question: "what am I doing wrong?" Many things, depending on the snippet. For instance, sometimes you're merging an enter selection based on an outer selection which was not merged... A comprehensive answer would be too long. Just have a look at my snippet and see that we create the update pattern for the outer level, and then the update pattern for the inner level, and so on...
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
add a comment |
First of all, according to the very creator of the DRY principle, DRY is not exactly the same of just avoiding repetition of code:
“Most people take DRY to mean you shouldn't duplicate code. That's not its intention. The idea behind DRY is far grander than that” (source)
In D3 you'll find yourself repeating some snippets again and again. That's not a problem per se, as long as you know what you're doing.
Back to your question:
You can simplify your code and avoid creating a mess if you know clearly what each selection is doing. Better than just saying each selection, we should say each update pattern.
In your case, you have two elements that should be created, updated and removed: <tr>
and <td>
.
So, you can write a very clean pattern for tr
s:
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
And another one for the td
s:
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
Since you're appending just one <td>
in each <tr>
(single-column), the last pattern can be greatly simplified. But let's keep it that way for now.
Also, don't forget to give meaningful names for the selections:
const body = d3.select("body");
const table = body.append("table");
Here is a demo:
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
Regarding your main question: "what am I doing wrong?" Many things, depending on the snippet. For instance, sometimes you're merging an enter selection based on an outer selection which was not merged... A comprehensive answer would be too long. Just have a look at my snippet and see that we create the update pattern for the outer level, and then the update pattern for the inner level, and so on...
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
add a comment |
First of all, according to the very creator of the DRY principle, DRY is not exactly the same of just avoiding repetition of code:
“Most people take DRY to mean you shouldn't duplicate code. That's not its intention. The idea behind DRY is far grander than that” (source)
In D3 you'll find yourself repeating some snippets again and again. That's not a problem per se, as long as you know what you're doing.
Back to your question:
You can simplify your code and avoid creating a mess if you know clearly what each selection is doing. Better than just saying each selection, we should say each update pattern.
In your case, you have two elements that should be created, updated and removed: <tr>
and <td>
.
So, you can write a very clean pattern for tr
s:
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
And another one for the td
s:
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
Since you're appending just one <td>
in each <tr>
(single-column), the last pattern can be greatly simplified. But let's keep it that way for now.
Also, don't forget to give meaningful names for the selections:
const body = d3.select("body");
const table = body.append("table");
Here is a demo:
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
Regarding your main question: "what am I doing wrong?" Many things, depending on the snippet. For instance, sometimes you're merging an enter selection based on an outer selection which was not merged... A comprehensive answer would be too long. Just have a look at my snippet and see that we create the update pattern for the outer level, and then the update pattern for the inner level, and so on...
First of all, according to the very creator of the DRY principle, DRY is not exactly the same of just avoiding repetition of code:
“Most people take DRY to mean you shouldn't duplicate code. That's not its intention. The idea behind DRY is far grander than that” (source)
In D3 you'll find yourself repeating some snippets again and again. That's not a problem per se, as long as you know what you're doing.
Back to your question:
You can simplify your code and avoid creating a mess if you know clearly what each selection is doing. Better than just saying each selection, we should say each update pattern.
In your case, you have two elements that should be created, updated and removed: <tr>
and <td>
.
So, you can write a very clean pattern for tr
s:
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
And another one for the td
s:
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
Since you're appending just one <td>
in each <tr>
(single-column), the last pattern can be greatly simplified. But let's keep it that way for now.
Also, don't forget to give meaningful names for the selections:
const body = d3.select("body");
const table = body.append("table");
Here is a demo:
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
Regarding your main question: "what am I doing wrong?" Many things, depending on the snippet. For instance, sometimes you're merging an enter selection based on an outer selection which was not merged... A comprehensive answer would be too long. Just have a look at my snippet and see that we create the update pattern for the outer level, and then the update pattern for the inner level, and so on...
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
const body = d3.select("body");
const table = body.append("table");
const data1 = [
name: "foo"
,
name: "bar"
,
name: "baz"
];
const data2 = [
name: "foobar"
,
name: "barbaz"
,
name: "bazfoo"
,
name: "foobaz"
];
d3.selectAll("button").on("click", function(_, i)
createTable(i ? data1 : data2)
)
function createTable(data)
let tr = table.selectAll("tr")
.data(data);
const trExit = tr.exit().remove();
const trEnter = tr.enter()
.append("tr");
tr = trEnter.merge(tr);
let td = tr.selectAll("td")
.data(function(d)
return [d]
);
const tdExit = td.exit().remove();
const tdEnter = td.enter()
.append("td");
td = tdEnter.merge(td);
td.text(function(d)
return d.name
);
;
table
font-family: verdana, arial, sans-serif;
font-size: 11px;
color: #333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
th
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
td
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Data 1</button><button>Data 2</button>
answered Mar 28 at 0:44
Gerardo FurtadoGerardo Furtado
74.1k8 gold badges54 silver badges102 bronze badges
74.1k8 gold badges54 silver badges102 bronze badges
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
add a comment |
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
Not sure where to put this, but I'll put it here: The key was as you showed: the update & enter rows needed to be merged first, and then that merge is used to create the update & enter TDs. Once I did that, it worked great. And no DRY violations! Marking as the accepted answer. Thank you for the swift and thorough reply!
– Patrick Mascari
Mar 28 at 13:54
add a comment |
Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.
Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55388212%2funable-to-merge-selections%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown