title: D3 v4 - force directed graph with threshold slider not working
body: <p>I'm trying to update this example <a href="http://jsfiddle.net/simonraper/TdHgx/?utm_source=website&amp;utm_medium=embed&amp;utm_campaign=TdHgx" rel="nofollow noreferrer">http://jsfiddle.net/simonraper/TdHgx/?utm_source=website&amp;utm_medium=embed&amp;utm_campaign=TdHgx</a> to d3 v4. </p>
  
  <pre><code>//Constants for the SVG
  var width = 500,
      height = 500;
  
  //Set up the colour scale
  var color = d3.scale.category20();
  
  //Set up the force layout
  var force = d3.layout.force()
      .charge(-120)
      .linkDistance(30)
      .size([width, height]);
  
  //Append a SVG to the body of the html page. Assign this SVG as an object to svg
  var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height)
  .on("dblclick", threshold);
  
  //Read the data from the mis element 
  var mis = document.getElementById('mis').innerHTML;
  graph = JSON.parse(mis);
  graphRec=JSON.parse(JSON.stringify(graph)); //Add this line
  
  //Creates the graph data structure out of the json data
  force.nodes(graph.nodes)
      .links(graph.links)
      .start();
  
  //Create all the line svgs but without locations yet
  var link = svg.selectAll(".link")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function (d) {
      return Math.sqrt(d.value);
  });
  
  //Do the same with the circles for the nodes - no 
  var node = svg.selectAll(".node")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("class", "node")
      .attr("r", 8)
      .style("fill", function (d) {
      return color(d.group);
  })
      .call(force.drag);
  
  
  //Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements
  force.on("tick", function () {
      link.attr("x1", function (d) {
          return d.source.x;
      })
          .attr("y1", function (d) {
          return d.source.y;
      })
          .attr("x2", function (d) {
          return d.target.x;
      })
          .attr("y2", function (d) {
          return d.target.y;
      });
  
      node.attr("cx", function (d) {
          return d.x;
      })
          .attr("cy", function (d) {
          return d.y;
      });
  });
  
  
  //---Insert-------
  
  //adjust threshold
  
  function threshold(thresh) {
      graph.links.splice(0, graph.links.length);
  
          for (var i = 0; i &lt; graphRec.links.length; i++) {
              if (graphRec.links[i].value &gt; thresh) {graph.links.push(graphRec.links[i]);}
          }
      restart();
  }
  
  
  //Restart the visualisation after any node and link changes
  
  function restart() {
  
      link = link.data(graph.links);
      link.exit().remove();
      link.enter().insert("line", ".node").attr("class", "link");
      node = node.data(graph.nodes);
      node.enter().insert("circle", ".cursor").attr("class", "node").attr("r", 5).call(force.drag);
      force.start();
  }
  //---End Insert---
  </code></pre>
  
  <p>The example works fine when I increase the threshold, but does not redraw the lines as expected when I decrease it. To see what I mean, look at the below codepen - try increasing threshold to max then back to min again. The lines look different / a lot more sparse.</p>
  
  <p><a href="https://codepen.io/mtsvelik/pen/rzxVrE" rel="nofollow noreferrer">https://codepen.io/mtsvelik/pen/rzxVrE</a></p>
  
  <p>How can I solve this problem?</p>
  
  <p>My Codepen example:</p>
  
  <pre><code>    //Read the data from the mis element 
      var graph = document.getElementById('json').innerHTML;
      graph = JSON.parse(graph);
      render(graph);  
  
  
  
      function render(graph){
  
      // Dimensions of sunburst.
      var radius = 6;
  
      var maxValue = d3.max(graph.links, function(d, i, data) {
          return d.value;
      });
  
      //sub-in max-value from
      d3.select("div").html('&lt;form class="force-control" ng-if="formControl"&gt;Link threshold 0 &lt;input type="range" id="thersholdSlider" name="points" value="0" min="0" max="'+ maxValue +'"&gt;'+ maxValue +'&lt;/form&gt;');
  
      document.getElementById("thersholdSlider").onchange = function() {threshold(this.value);};
  
      var svg = d3.select("svg");
      var width = svg.attr("width");
      var height = svg.attr("height");
  
  
      console.log(graph);
      var graphRec = JSON.parse(JSON.stringify(graph)); //Add this line       
      //graphRec = graph; //Add this line         
      console.log(graphRec);
  
      var simulation = d3.forceSimulation()
              .force("link", d3.forceLink().id(function(d) { return d.id; }))
              .force("charge", d3.forceManyBody().strength(Number(-1000 + (1.25*graph.links.length))))  //default force is -30, making weaker to increase size of chart
              .force("center", d3.forceCenter(width / 2, height / 2));
  
      var link = svg.append("g")
                .attr("class", "links")
              .selectAll("line")
              .data(graph.links)
              .enter().append("line")
                .attr("class", "link")
                .attr("stroke-width", function(d) { return Math.sqrt(d.value); });
  
      var node = svg.append("g")
                .attr("class", "nodes")
              .selectAll("circle")
              .data(graph.nodes)
              .enter().append("circle")
                .attr("class", "node")
                .attr("r", radius)
                .attr("fill", function(d) { return d.color; })
                .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    .on("end", dragended));
  
            node.append("title")
                .text(function(d) { return d.id; });
  
            simulation
                .nodes(graph.nodes)
                .on("tick", ticked);
  
            simulation.force("link")
                .links(graph.links);
  
      console.log(link.data(graph.links));
  
      //drawLegend(labelCounts, allLabels, totalJourneys);  //need a copy of the buildHeirarchy function from sunburst-draw.js to extract these vars
  
      function ticked() {
              link
                  .attr("x1", function(d) { return d.source.x; })
                  .attr("y1", function(d) { return d.source.y; })
                  .attr("x2", function(d) { return d.target.x; })
                  .attr("y2", function(d) { return d.target.y; });
  
              node
                  .attr("cx", function(d) { return d.x; })
                  .attr("cy", function(d) { return d.y; });
            }
  
      function dragstarted(d) {
            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
          }
  
      function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
          }
  
      function dragended(d) {
            if (!d3.event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
          }
  
  
  
      function threshold(thresh) {
              thresh = Number(thresh);
              graph.links.splice(0, graph.links.length);
                  for (var i = 0; i &lt; graphRec.links.length; i++) {
                      if (graphRec.links[i].value &gt; thresh) {graph.links.push(graphRec.links[i]);}
                  }
  
              console.log(graph.links);
              /*var threshold_links = graph.links.filter(function(d){ return (d.value &gt; thresh);});
              console.log(graph.links);
  
              restart(threshold_links);*/
              restart();
  
  
          }
  
  
      //Restart the visualisation after any node and link changes
  //  function restart(threshold_links) {
      function restart() {    
  
              //DATA JOIN 
              //link = link.data(threshold_links);
              link = link.data(graph.links);    
              console.log(link); 
  
              //EXIT
              link.exit().remove();
              console.log(link);
  
              // ENTER - https://bl.ocks.org/colbenkharrl/21b3808492b93a21de841bc5ceac4e47
              // Create new links as needed.  
              link = link.enter().append("line")
              .attr("class", "link")
              //.attr("stroke-width", function(d){ return d.})
              .merge(link);           
              console.log(link);
  
  
              // DATA JOIN
              node = node.data(graph.nodes);
  
              /*
              // EXIT
              node.exit().remove();
  
              // ENTER
              node = node.enter().append("circle")
                  .attr("class", "node")
                  .attr("r", radius)
                  .attr("fill", function(d) {return d.color;})
                  .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    .on("end", dragended)
                  )
                  .merge(node);
  
              node.append("title")
              .text(function(d) { return d.id; });
              */          
  
            simulation
                .nodes(graph.nodes)
                .on("tick", ticked);
  
            simulation.force("link")
                .links(graph.links);
  
              simulation.alphaTarget(0.3).restart();
          }
  
      }
  </code></pre>