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;








1















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.










share|improve this question
































    1















    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.










    share|improve this question




























      1












      1








      1








      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.










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Mar 28 at 13:58







      Patrick Mascari

















      asked Mar 27 at 23:55









      Patrick MascariPatrick Mascari

      83 bronze badges




      83 bronze badges

























          1 Answer
          1






          active

          oldest

          votes


















          1















          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 trs:



          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 tds:



          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...






          share|improve this answer

























          • 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










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



          );













          draft saved

          draft discarded


















          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









          1















          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 trs:



          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 tds:



          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...






          share|improve this answer

























          • 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















          1















          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 trs:



          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 tds:



          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...






          share|improve this answer

























          • 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













          1














          1










          1









          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 trs:



          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 tds:



          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...






          share|improve this answer













          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 trs:



          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 tds:



          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>






          share|improve this answer












          share|improve this answer



          share|improve this answer










          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

















          • 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








          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.



















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

          Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

          Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript