Update the line charts without deleting the svg

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












I have created line charts in d3.js which show the trend of two measures. Each chart consists of two linecharts and the graph is repeated as many times as there are countries.



So my dataset is this:



var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
];


The result is:



enter image description here



The first column corresponds to Germany, the second to France and the third to Spain. The trend of the firstVal data is represented by a blue line, the trend of the secondVal data is represented by a tomato line.



At the top there are two radio buttons: when the user changes the selection, the graphs are updated. In reality they are eliminated and then recreated and this is certainly not a good solution.
I wanted to ask you how I could do to avoid having to delete the entire SVG but only modify the element d.



Here is the complete code.



index.html:



<!DOCTYPE html>
<html lang='en'>

<head>
<meta charset='utf-8'>
<link rel="stylesheet" href="style.css">
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<script src='https://d3js.org/d3.v5.min.js'></script>
</head>

<body>

<div class='row'>
<div class='col' id='type-selector-container'>
<div class='custom-control custom-radio custom-control-inline'>
<input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
<label class='custom-control-label' for='rb-apple'>APPLE</label>
</div>
<div class='custom-control custom-radio custom-control-inline'>
<input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
<label class='custom-control-label' for='rb-pear'>PEAR</label>
</div>
</div>
</div>

<div id='doublelineDiv'></div>

<script src="script.js"></script>
</body>

</html>


script.js:



var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
];
console.log('data', data);

var countries = ['Germany', 'France', 'Spain'];
var numCountries = countries.length;
var dataSmall;
var minFirst;
var maxFirst;
var minSecond;
var maxSecond;
var years;
var typeSelected = 'APPLE';
var widthSingleChart = 130;
var heightSingleChart = 80;

createSmallMultiples();

function createSmallMultiples()
dataSmall = data.filter(function(d)
return d.type == typeSelected;
);
console.log('dataSmall', dataSmall);

var firsts = ;
var seconds = ;
dataSmall.filter(function(d)
if(!isNaN(d.firstVal))
firsts.push(Number(d.firstVal));

if(!isNaN(d.secondVal))
seconds.push(Number(d.secondVal));

);

minFirst = Math.min(...firsts);
maxFirst = Math.max(...firsts);
console.log('firsts [' + minFirst + ', ' + maxFirst + ']');

if(seconds.length > 1)
minSecond = Math.min(...seconds);
maxSecond = Math.max(...seconds);

else
minSecond = 0;
maxSecond = 0;

minSecond = Math.min(...seconds);
maxSecond = Math.max(...seconds);
console.log('seconds [' + minSecond + ', ' + maxSecond + ']');

years = [...new Set(dataSmall.map(function(d)
return +d.year;
))].sort();

console.log('countries', countries);
for(var i = 0; i < numCountries; i++)
var country = countries[i];
singleLineChart(country);



var updateRadio = function()
typeSelected = this.value;
d3.select('#doublelineDiv').html('');
createSmallMultiples();

d3.selectAll('input[name=type-selector]').on('change', updateRadio);


// Single line chart
function singleLineChart(thisCountry)
console.log('singleLineChart - ', thisCountry);

// filter data by country
var dataSmallFilter = dataSmall.filter(function(d)
return d.country == thisCountry;
);
console.log('dataSmallFilter', dataSmallFilter);

var margin = top: 0, right: 0, bottom: 3, left: 10;

// svg for both line chart (coverage and cases)
var doublelineDiv = d3.select('#doublelineDiv');
var doublelineSvg = doublelineDiv.append('svg')
.attr('class', thisCountry)
.attr('width', widthSingleChart)
.attr('height', heightSingleChart);

// FIRST LINE
var doublelineSvgFirst = doublelineSvg
.append('svg')
.attr('class', 'firstLine')
.attr('width', widthSingleChart)
.attr('height', heightSingleChart/2);

var width = doublelineSvgFirst.attr('width') - margin.left - margin.right;
var height = doublelineSvgFirst.attr('height') - margin.top - margin.bottom;
var doublelineSvgFirstG = doublelineSvgFirst.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

var x = d3.scaleTime()
.rangeRound([0, width]);

var yFirst = d3.scaleLinear()
.rangeRound([height, 0]);

x.domain(d3.extent(dataSmallFilter, function(d)
return d.year;
));
yFirst.domain([minFirst, 100]);

var xAxis = d3.axisBottom(x).tickSize(0, 0);
var yAxisFirst = d3.axisLeft(yFirst);

doublelineSvgFirstG.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');

doublelineSvgFirstG.append('g')
.attr('class', 'y axis')
.call(yAxisFirst)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');

var lineFirst = d3.line()
.x(function(d)
return x(d.year);
)
.y(function(d)
return yFirst(d.firstVal);
)
.defined(function(d)
if(isNaN(d.firstVal))
d.firstVal = 0;

return d.firstVal !== 0;
);

var linesFirst = doublelineSvgFirstG.attr('transform', function(d)
return 'translate(' + margin.left + ', ' + margin.top + ')';
);

console.log('lineFirst', lineFirst);

linesFirst.append('path')
.data([dataSmallFilter])
.attr('class', 'mylines')
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1)
.attr('d', lineFirst);

// SECOND LINE
var doublelineSvgSecond = doublelineSvg
.append('svg')
.attr('class', 'secondLine')
.attr('transform', 'translate(0, ' + heightSingleChart/2 + ')')
.attr('width', widthSingleChart)
.attr('height', heightSingleChart/2);

var width = doublelineSvgSecond.attr('width') - margin.left - margin.right;
var height = doublelineSvgSecond.attr('height') - margin.top - margin.bottom;
var doublelineSvgSecondG = doublelineSvgSecond.append('g');//.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

var ySecond = d3.scaleSqrt()
.rangeRound([height, 0]);

x.domain(d3.extent(dataSmallFilter, function(d)
return d.year;
));
ySecond.domain([0, maxSecond]);

var xAxis = d3.axisBottom(x).tickSize(0, 0);
var yAxisSecond = d3.axisLeft(ySecond);

doublelineSvgSecondG.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');

doublelineSvgSecondG.append('g')
.attr('class', 'y axis')
.call(yAxisSecond)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');

var lineSecond = d3.line()
.x(function(d)
return x(d.year);
)
.y(function(d)
return ySecond(d.secondVal);
)
.defined(function(d)
if(isNaN(d.secondVal))
d.secondVal = 0;

return d.secondVal !== 0;
);

var linesSecond = doublelineSvgSecondG.attr('transform', function(d)
return 'translate(' + margin.left + ', ' + margin.top + ')';
);

console.log('lineSecond', lineSecond);

linesSecond.append('path')
.data([dataSmallFilter])
.attr('class', 'mylines')
.attr('fill', 'none')
.attr('stroke', 'tomato')
.attr('stroke-width', 1)
.attr('d', lineSecond);







share|improve this question



























    up vote
    3
    down vote

    favorite












    I have created line charts in d3.js which show the trend of two measures. Each chart consists of two linecharts and the graph is repeated as many times as there are countries.



    So my dataset is this:



    var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
    type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
    type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
    type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
    type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
    type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
    type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
    type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
    type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
    type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
    type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
    type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
    type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
    type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
    type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
    type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
    type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
    type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
    type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
    type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
    type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
    type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
    type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
    type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
    ];


    The result is:



    enter image description here



    The first column corresponds to Germany, the second to France and the third to Spain. The trend of the firstVal data is represented by a blue line, the trend of the secondVal data is represented by a tomato line.



    At the top there are two radio buttons: when the user changes the selection, the graphs are updated. In reality they are eliminated and then recreated and this is certainly not a good solution.
    I wanted to ask you how I could do to avoid having to delete the entire SVG but only modify the element d.



    Here is the complete code.



    index.html:



    <!DOCTYPE html>
    <html lang='en'>

    <head>
    <meta charset='utf-8'>
    <link rel="stylesheet" href="style.css">
    <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
    <script src='https://d3js.org/d3.v5.min.js'></script>
    </head>

    <body>

    <div class='row'>
    <div class='col' id='type-selector-container'>
    <div class='custom-control custom-radio custom-control-inline'>
    <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
    <label class='custom-control-label' for='rb-apple'>APPLE</label>
    </div>
    <div class='custom-control custom-radio custom-control-inline'>
    <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
    <label class='custom-control-label' for='rb-pear'>PEAR</label>
    </div>
    </div>
    </div>

    <div id='doublelineDiv'></div>

    <script src="script.js"></script>
    </body>

    </html>


    script.js:



    var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
    type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
    type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
    type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
    type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
    type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
    type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
    type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
    type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
    type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
    type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
    type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
    type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
    type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
    type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
    type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
    type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
    type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
    type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
    type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
    type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
    type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
    type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
    type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
    ];
    console.log('data', data);

    var countries = ['Germany', 'France', 'Spain'];
    var numCountries = countries.length;
    var dataSmall;
    var minFirst;
    var maxFirst;
    var minSecond;
    var maxSecond;
    var years;
    var typeSelected = 'APPLE';
    var widthSingleChart = 130;
    var heightSingleChart = 80;

    createSmallMultiples();

    function createSmallMultiples()
    dataSmall = data.filter(function(d)
    return d.type == typeSelected;
    );
    console.log('dataSmall', dataSmall);

    var firsts = ;
    var seconds = ;
    dataSmall.filter(function(d)
    if(!isNaN(d.firstVal))
    firsts.push(Number(d.firstVal));

    if(!isNaN(d.secondVal))
    seconds.push(Number(d.secondVal));

    );

    minFirst = Math.min(...firsts);
    maxFirst = Math.max(...firsts);
    console.log('firsts [' + minFirst + ', ' + maxFirst + ']');

    if(seconds.length > 1)
    minSecond = Math.min(...seconds);
    maxSecond = Math.max(...seconds);

    else
    minSecond = 0;
    maxSecond = 0;

    minSecond = Math.min(...seconds);
    maxSecond = Math.max(...seconds);
    console.log('seconds [' + minSecond + ', ' + maxSecond + ']');

    years = [...new Set(dataSmall.map(function(d)
    return +d.year;
    ))].sort();

    console.log('countries', countries);
    for(var i = 0; i < numCountries; i++)
    var country = countries[i];
    singleLineChart(country);



    var updateRadio = function()
    typeSelected = this.value;
    d3.select('#doublelineDiv').html('');
    createSmallMultiples();

    d3.selectAll('input[name=type-selector]').on('change', updateRadio);


    // Single line chart
    function singleLineChart(thisCountry)
    console.log('singleLineChart - ', thisCountry);

    // filter data by country
    var dataSmallFilter = dataSmall.filter(function(d)
    return d.country == thisCountry;
    );
    console.log('dataSmallFilter', dataSmallFilter);

    var margin = top: 0, right: 0, bottom: 3, left: 10;

    // svg for both line chart (coverage and cases)
    var doublelineDiv = d3.select('#doublelineDiv');
    var doublelineSvg = doublelineDiv.append('svg')
    .attr('class', thisCountry)
    .attr('width', widthSingleChart)
    .attr('height', heightSingleChart);

    // FIRST LINE
    var doublelineSvgFirst = doublelineSvg
    .append('svg')
    .attr('class', 'firstLine')
    .attr('width', widthSingleChart)
    .attr('height', heightSingleChart/2);

    var width = doublelineSvgFirst.attr('width') - margin.left - margin.right;
    var height = doublelineSvgFirst.attr('height') - margin.top - margin.bottom;
    var doublelineSvgFirstG = doublelineSvgFirst.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

    var x = d3.scaleTime()
    .rangeRound([0, width]);

    var yFirst = d3.scaleLinear()
    .rangeRound([height, 0]);

    x.domain(d3.extent(dataSmallFilter, function(d)
    return d.year;
    ));
    yFirst.domain([minFirst, 100]);

    var xAxis = d3.axisBottom(x).tickSize(0, 0);
    var yAxisFirst = d3.axisLeft(yFirst);

    doublelineSvgFirstG.append('g')
    .attr('class', 'x axis')
    .attr('transform', 'translate(0, ' + height + ')')
    .call(xAxis)
    .selectAll('text')
    .style('text-anchor', 'end')
    .attr('dx', '-.8em')
    .attr('dy', '.15em')
    .attr('transform', 'rotate(-65)');

    doublelineSvgFirstG.append('g')
    .attr('class', 'y axis')
    .call(yAxisFirst)
    .append('text')
    .attr('transform', 'rotate(-90)')
    .attr('y', 6)
    .attr('dy', '.71em')
    .style('text-anchor', 'end');

    var lineFirst = d3.line()
    .x(function(d)
    return x(d.year);
    )
    .y(function(d)
    return yFirst(d.firstVal);
    )
    .defined(function(d)
    if(isNaN(d.firstVal))
    d.firstVal = 0;

    return d.firstVal !== 0;
    );

    var linesFirst = doublelineSvgFirstG.attr('transform', function(d)
    return 'translate(' + margin.left + ', ' + margin.top + ')';
    );

    console.log('lineFirst', lineFirst);

    linesFirst.append('path')
    .data([dataSmallFilter])
    .attr('class', 'mylines')
    .attr('fill', 'none')
    .attr('stroke', 'steelblue')
    .attr('stroke-width', 1)
    .attr('d', lineFirst);

    // SECOND LINE
    var doublelineSvgSecond = doublelineSvg
    .append('svg')
    .attr('class', 'secondLine')
    .attr('transform', 'translate(0, ' + heightSingleChart/2 + ')')
    .attr('width', widthSingleChart)
    .attr('height', heightSingleChart/2);

    var width = doublelineSvgSecond.attr('width') - margin.left - margin.right;
    var height = doublelineSvgSecond.attr('height') - margin.top - margin.bottom;
    var doublelineSvgSecondG = doublelineSvgSecond.append('g');//.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

    var ySecond = d3.scaleSqrt()
    .rangeRound([height, 0]);

    x.domain(d3.extent(dataSmallFilter, function(d)
    return d.year;
    ));
    ySecond.domain([0, maxSecond]);

    var xAxis = d3.axisBottom(x).tickSize(0, 0);
    var yAxisSecond = d3.axisLeft(ySecond);

    doublelineSvgSecondG.append('g')
    .attr('class', 'x axis')
    .attr('transform', 'translate(0, ' + height + ')')
    .call(xAxis)
    .selectAll('text')
    .style('text-anchor', 'end')
    .attr('dx', '-.8em')
    .attr('dy', '.15em')
    .attr('transform', 'rotate(-65)');

    doublelineSvgSecondG.append('g')
    .attr('class', 'y axis')
    .call(yAxisSecond)
    .append('text')
    .attr('transform', 'rotate(-90)')
    .attr('y', 6)
    .attr('dy', '.71em')
    .style('text-anchor', 'end');

    var lineSecond = d3.line()
    .x(function(d)
    return x(d.year);
    )
    .y(function(d)
    return ySecond(d.secondVal);
    )
    .defined(function(d)
    if(isNaN(d.secondVal))
    d.secondVal = 0;

    return d.secondVal !== 0;
    );

    var linesSecond = doublelineSvgSecondG.attr('transform', function(d)
    return 'translate(' + margin.left + ', ' + margin.top + ')';
    );

    console.log('lineSecond', lineSecond);

    linesSecond.append('path')
    .data([dataSmallFilter])
    .attr('class', 'mylines')
    .attr('fill', 'none')
    .attr('stroke', 'tomato')
    .attr('stroke-width', 1)
    .attr('d', lineSecond);







    share|improve this question























      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      I have created line charts in d3.js which show the trend of two measures. Each chart consists of two linecharts and the graph is repeated as many times as there are countries.



      So my dataset is this:



      var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
      type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
      type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
      type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
      type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
      type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
      type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
      type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
      type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
      type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
      type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
      type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
      type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
      type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
      type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
      type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
      type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
      type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
      type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
      type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
      type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
      type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
      ];


      The result is:



      enter image description here



      The first column corresponds to Germany, the second to France and the third to Spain. The trend of the firstVal data is represented by a blue line, the trend of the secondVal data is represented by a tomato line.



      At the top there are two radio buttons: when the user changes the selection, the graphs are updated. In reality they are eliminated and then recreated and this is certainly not a good solution.
      I wanted to ask you how I could do to avoid having to delete the entire SVG but only modify the element d.



      Here is the complete code.



      index.html:



      <!DOCTYPE html>
      <html lang='en'>

      <head>
      <meta charset='utf-8'>
      <link rel="stylesheet" href="style.css">
      <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
      <script src='https://d3js.org/d3.v5.min.js'></script>
      </head>

      <body>

      <div class='row'>
      <div class='col' id='type-selector-container'>
      <div class='custom-control custom-radio custom-control-inline'>
      <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
      <label class='custom-control-label' for='rb-apple'>APPLE</label>
      </div>
      <div class='custom-control custom-radio custom-control-inline'>
      <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
      <label class='custom-control-label' for='rb-pear'>PEAR</label>
      </div>
      </div>
      </div>

      <div id='doublelineDiv'></div>

      <script src="script.js"></script>
      </body>

      </html>


      script.js:



      var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
      type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
      type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
      type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
      type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
      type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
      type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
      type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
      type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
      type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
      type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
      type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
      type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
      type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
      type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
      type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
      type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
      type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
      type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
      type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
      type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
      type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
      ];
      console.log('data', data);

      var countries = ['Germany', 'France', 'Spain'];
      var numCountries = countries.length;
      var dataSmall;
      var minFirst;
      var maxFirst;
      var minSecond;
      var maxSecond;
      var years;
      var typeSelected = 'APPLE';
      var widthSingleChart = 130;
      var heightSingleChart = 80;

      createSmallMultiples();

      function createSmallMultiples()
      dataSmall = data.filter(function(d)
      return d.type == typeSelected;
      );
      console.log('dataSmall', dataSmall);

      var firsts = ;
      var seconds = ;
      dataSmall.filter(function(d)
      if(!isNaN(d.firstVal))
      firsts.push(Number(d.firstVal));

      if(!isNaN(d.secondVal))
      seconds.push(Number(d.secondVal));

      );

      minFirst = Math.min(...firsts);
      maxFirst = Math.max(...firsts);
      console.log('firsts [' + minFirst + ', ' + maxFirst + ']');

      if(seconds.length > 1)
      minSecond = Math.min(...seconds);
      maxSecond = Math.max(...seconds);

      else
      minSecond = 0;
      maxSecond = 0;

      minSecond = Math.min(...seconds);
      maxSecond = Math.max(...seconds);
      console.log('seconds [' + minSecond + ', ' + maxSecond + ']');

      years = [...new Set(dataSmall.map(function(d)
      return +d.year;
      ))].sort();

      console.log('countries', countries);
      for(var i = 0; i < numCountries; i++)
      var country = countries[i];
      singleLineChart(country);



      var updateRadio = function()
      typeSelected = this.value;
      d3.select('#doublelineDiv').html('');
      createSmallMultiples();

      d3.selectAll('input[name=type-selector]').on('change', updateRadio);


      // Single line chart
      function singleLineChart(thisCountry)
      console.log('singleLineChart - ', thisCountry);

      // filter data by country
      var dataSmallFilter = dataSmall.filter(function(d)
      return d.country == thisCountry;
      );
      console.log('dataSmallFilter', dataSmallFilter);

      var margin = top: 0, right: 0, bottom: 3, left: 10;

      // svg for both line chart (coverage and cases)
      var doublelineDiv = d3.select('#doublelineDiv');
      var doublelineSvg = doublelineDiv.append('svg')
      .attr('class', thisCountry)
      .attr('width', widthSingleChart)
      .attr('height', heightSingleChart);

      // FIRST LINE
      var doublelineSvgFirst = doublelineSvg
      .append('svg')
      .attr('class', 'firstLine')
      .attr('width', widthSingleChart)
      .attr('height', heightSingleChart/2);

      var width = doublelineSvgFirst.attr('width') - margin.left - margin.right;
      var height = doublelineSvgFirst.attr('height') - margin.top - margin.bottom;
      var doublelineSvgFirstG = doublelineSvgFirst.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

      var x = d3.scaleTime()
      .rangeRound([0, width]);

      var yFirst = d3.scaleLinear()
      .rangeRound([height, 0]);

      x.domain(d3.extent(dataSmallFilter, function(d)
      return d.year;
      ));
      yFirst.domain([minFirst, 100]);

      var xAxis = d3.axisBottom(x).tickSize(0, 0);
      var yAxisFirst = d3.axisLeft(yFirst);

      doublelineSvgFirstG.append('g')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0, ' + height + ')')
      .call(xAxis)
      .selectAll('text')
      .style('text-anchor', 'end')
      .attr('dx', '-.8em')
      .attr('dy', '.15em')
      .attr('transform', 'rotate(-65)');

      doublelineSvgFirstG.append('g')
      .attr('class', 'y axis')
      .call(yAxisFirst)
      .append('text')
      .attr('transform', 'rotate(-90)')
      .attr('y', 6)
      .attr('dy', '.71em')
      .style('text-anchor', 'end');

      var lineFirst = d3.line()
      .x(function(d)
      return x(d.year);
      )
      .y(function(d)
      return yFirst(d.firstVal);
      )
      .defined(function(d)
      if(isNaN(d.firstVal))
      d.firstVal = 0;

      return d.firstVal !== 0;
      );

      var linesFirst = doublelineSvgFirstG.attr('transform', function(d)
      return 'translate(' + margin.left + ', ' + margin.top + ')';
      );

      console.log('lineFirst', lineFirst);

      linesFirst.append('path')
      .data([dataSmallFilter])
      .attr('class', 'mylines')
      .attr('fill', 'none')
      .attr('stroke', 'steelblue')
      .attr('stroke-width', 1)
      .attr('d', lineFirst);

      // SECOND LINE
      var doublelineSvgSecond = doublelineSvg
      .append('svg')
      .attr('class', 'secondLine')
      .attr('transform', 'translate(0, ' + heightSingleChart/2 + ')')
      .attr('width', widthSingleChart)
      .attr('height', heightSingleChart/2);

      var width = doublelineSvgSecond.attr('width') - margin.left - margin.right;
      var height = doublelineSvgSecond.attr('height') - margin.top - margin.bottom;
      var doublelineSvgSecondG = doublelineSvgSecond.append('g');//.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

      var ySecond = d3.scaleSqrt()
      .rangeRound([height, 0]);

      x.domain(d3.extent(dataSmallFilter, function(d)
      return d.year;
      ));
      ySecond.domain([0, maxSecond]);

      var xAxis = d3.axisBottom(x).tickSize(0, 0);
      var yAxisSecond = d3.axisLeft(ySecond);

      doublelineSvgSecondG.append('g')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0, ' + height + ')')
      .call(xAxis)
      .selectAll('text')
      .style('text-anchor', 'end')
      .attr('dx', '-.8em')
      .attr('dy', '.15em')
      .attr('transform', 'rotate(-65)');

      doublelineSvgSecondG.append('g')
      .attr('class', 'y axis')
      .call(yAxisSecond)
      .append('text')
      .attr('transform', 'rotate(-90)')
      .attr('y', 6)
      .attr('dy', '.71em')
      .style('text-anchor', 'end');

      var lineSecond = d3.line()
      .x(function(d)
      return x(d.year);
      )
      .y(function(d)
      return ySecond(d.secondVal);
      )
      .defined(function(d)
      if(isNaN(d.secondVal))
      d.secondVal = 0;

      return d.secondVal !== 0;
      );

      var linesSecond = doublelineSvgSecondG.attr('transform', function(d)
      return 'translate(' + margin.left + ', ' + margin.top + ')';
      );

      console.log('lineSecond', lineSecond);

      linesSecond.append('path')
      .data([dataSmallFilter])
      .attr('class', 'mylines')
      .attr('fill', 'none')
      .attr('stroke', 'tomato')
      .attr('stroke-width', 1)
      .attr('d', lineSecond);







      share|improve this question













      I have created line charts in d3.js which show the trend of two measures. Each chart consists of two linecharts and the graph is repeated as many times as there are countries.



      So my dataset is this:



      var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
      type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
      type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
      type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
      type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
      type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
      type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
      type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
      type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
      type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
      type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
      type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
      type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
      type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
      type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
      type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
      type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
      type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
      type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
      type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
      type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
      type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
      ];


      The result is:



      enter image description here



      The first column corresponds to Germany, the second to France and the third to Spain. The trend of the firstVal data is represented by a blue line, the trend of the secondVal data is represented by a tomato line.



      At the top there are two radio buttons: when the user changes the selection, the graphs are updated. In reality they are eliminated and then recreated and this is certainly not a good solution.
      I wanted to ask you how I could do to avoid having to delete the entire SVG but only modify the element d.



      Here is the complete code.



      index.html:



      <!DOCTYPE html>
      <html lang='en'>

      <head>
      <meta charset='utf-8'>
      <link rel="stylesheet" href="style.css">
      <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
      <script src='https://d3js.org/d3.v5.min.js'></script>
      </head>

      <body>

      <div class='row'>
      <div class='col' id='type-selector-container'>
      <div class='custom-control custom-radio custom-control-inline'>
      <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
      <label class='custom-control-label' for='rb-apple'>APPLE</label>
      </div>
      <div class='custom-control custom-radio custom-control-inline'>
      <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
      <label class='custom-control-label' for='rb-pear'>PEAR</label>
      </div>
      </div>
      </div>

      <div id='doublelineDiv'></div>

      <script src="script.js"></script>
      </body>

      </html>


      script.js:



      var data = [type:'APPLE', country:'Germany', year:'2000', firstVal:'99.5', secondVal:'4',
      type:'APPLE', country:'Germany', year:'2001', firstVal:'98', secondVal:'2',
      type:'APPLE', country:'Germany', year:'2002', firstVal:'84', secondVal:'2.3',
      type:'APPLE', country:'Germany', year:'2003', firstVal:'86', secondVal:'2',
      type:'PEAR', country:'Germany', year:'2000', firstVal:'99', secondVal:'3',
      type:'PEAR', country:'Germany', year:'2001', firstVal:'98', secondVal:'10',
      type:'PEAR', country:'Germany', year:'2002', firstVal:'97', secondVal:'8',
      type:'PEAR', country:'Germany', year:'2003', firstVal:'98', secondVal:'10.9',
      type:'APPLE', country:'France', year:'2000', firstVal:'NaN', secondVal:'4',
      type:'APPLE', country:'France', year:'2001', firstVal:'85', secondVal:'50',
      type:'APPLE', country:'France', year:'2002', firstVal:'86', secondVal:'27',
      type:'APPLE', country:'France', year:'2003', firstVal:'86', secondVal:'5',
      type:'PEAR', country:'France', year:'2000', firstVal:'87', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2001', firstVal:'99', secondVal:'NaN',
      type:'PEAR', country:'France', year:'2002', firstVal:'70', secondVal:'5',
      type:'PEAR', country:'France', year:'2003', firstVal:'90', secondVal:'9',
      type:'APPLE', country:'Spain', year:'2000', firstVal:'88', secondVal:'14',
      type:'APPLE', country:'Spain', year:'2001', firstVal:'85', secondVal:'2',
      type:'APPLE', country:'Spain', year:'2002', firstVal:'86', secondVal:'1',
      type:'APPLE', country:'Spain', year:'2003', firstVal:'84', secondVal:'5',
      type:'PEAR', country:'Spain', year:'2000', firstVal:'65', secondVal:'3',
      type:'PEAR', country:'Spain', year:'2001', firstVal:'NaN', secondVal:'4',
      type:'PEAR', country:'Spain', year:'2002', firstVal:'95', secondVal:'2',
      type:'PEAR', country:'Spain', year:'2003', firstVal:'78', secondVal:'1'
      ];
      console.log('data', data);

      var countries = ['Germany', 'France', 'Spain'];
      var numCountries = countries.length;
      var dataSmall;
      var minFirst;
      var maxFirst;
      var minSecond;
      var maxSecond;
      var years;
      var typeSelected = 'APPLE';
      var widthSingleChart = 130;
      var heightSingleChart = 80;

      createSmallMultiples();

      function createSmallMultiples()
      dataSmall = data.filter(function(d)
      return d.type == typeSelected;
      );
      console.log('dataSmall', dataSmall);

      var firsts = ;
      var seconds = ;
      dataSmall.filter(function(d)
      if(!isNaN(d.firstVal))
      firsts.push(Number(d.firstVal));

      if(!isNaN(d.secondVal))
      seconds.push(Number(d.secondVal));

      );

      minFirst = Math.min(...firsts);
      maxFirst = Math.max(...firsts);
      console.log('firsts [' + minFirst + ', ' + maxFirst + ']');

      if(seconds.length > 1)
      minSecond = Math.min(...seconds);
      maxSecond = Math.max(...seconds);

      else
      minSecond = 0;
      maxSecond = 0;

      minSecond = Math.min(...seconds);
      maxSecond = Math.max(...seconds);
      console.log('seconds [' + minSecond + ', ' + maxSecond + ']');

      years = [...new Set(dataSmall.map(function(d)
      return +d.year;
      ))].sort();

      console.log('countries', countries);
      for(var i = 0; i < numCountries; i++)
      var country = countries[i];
      singleLineChart(country);



      var updateRadio = function()
      typeSelected = this.value;
      d3.select('#doublelineDiv').html('');
      createSmallMultiples();

      d3.selectAll('input[name=type-selector]').on('change', updateRadio);


      // Single line chart
      function singleLineChart(thisCountry)
      console.log('singleLineChart - ', thisCountry);

      // filter data by country
      var dataSmallFilter = dataSmall.filter(function(d)
      return d.country == thisCountry;
      );
      console.log('dataSmallFilter', dataSmallFilter);

      var margin = top: 0, right: 0, bottom: 3, left: 10;

      // svg for both line chart (coverage and cases)
      var doublelineDiv = d3.select('#doublelineDiv');
      var doublelineSvg = doublelineDiv.append('svg')
      .attr('class', thisCountry)
      .attr('width', widthSingleChart)
      .attr('height', heightSingleChart);

      // FIRST LINE
      var doublelineSvgFirst = doublelineSvg
      .append('svg')
      .attr('class', 'firstLine')
      .attr('width', widthSingleChart)
      .attr('height', heightSingleChart/2);

      var width = doublelineSvgFirst.attr('width') - margin.left - margin.right;
      var height = doublelineSvgFirst.attr('height') - margin.top - margin.bottom;
      var doublelineSvgFirstG = doublelineSvgFirst.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

      var x = d3.scaleTime()
      .rangeRound([0, width]);

      var yFirst = d3.scaleLinear()
      .rangeRound([height, 0]);

      x.domain(d3.extent(dataSmallFilter, function(d)
      return d.year;
      ));
      yFirst.domain([minFirst, 100]);

      var xAxis = d3.axisBottom(x).tickSize(0, 0);
      var yAxisFirst = d3.axisLeft(yFirst);

      doublelineSvgFirstG.append('g')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0, ' + height + ')')
      .call(xAxis)
      .selectAll('text')
      .style('text-anchor', 'end')
      .attr('dx', '-.8em')
      .attr('dy', '.15em')
      .attr('transform', 'rotate(-65)');

      doublelineSvgFirstG.append('g')
      .attr('class', 'y axis')
      .call(yAxisFirst)
      .append('text')
      .attr('transform', 'rotate(-90)')
      .attr('y', 6)
      .attr('dy', '.71em')
      .style('text-anchor', 'end');

      var lineFirst = d3.line()
      .x(function(d)
      return x(d.year);
      )
      .y(function(d)
      return yFirst(d.firstVal);
      )
      .defined(function(d)
      if(isNaN(d.firstVal))
      d.firstVal = 0;

      return d.firstVal !== 0;
      );

      var linesFirst = doublelineSvgFirstG.attr('transform', function(d)
      return 'translate(' + margin.left + ', ' + margin.top + ')';
      );

      console.log('lineFirst', lineFirst);

      linesFirst.append('path')
      .data([dataSmallFilter])
      .attr('class', 'mylines')
      .attr('fill', 'none')
      .attr('stroke', 'steelblue')
      .attr('stroke-width', 1)
      .attr('d', lineFirst);

      // SECOND LINE
      var doublelineSvgSecond = doublelineSvg
      .append('svg')
      .attr('class', 'secondLine')
      .attr('transform', 'translate(0, ' + heightSingleChart/2 + ')')
      .attr('width', widthSingleChart)
      .attr('height', heightSingleChart/2);

      var width = doublelineSvgSecond.attr('width') - margin.left - margin.right;
      var height = doublelineSvgSecond.attr('height') - margin.top - margin.bottom;
      var doublelineSvgSecondG = doublelineSvgSecond.append('g');//.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

      var ySecond = d3.scaleSqrt()
      .rangeRound([height, 0]);

      x.domain(d3.extent(dataSmallFilter, function(d)
      return d.year;
      ));
      ySecond.domain([0, maxSecond]);

      var xAxis = d3.axisBottom(x).tickSize(0, 0);
      var yAxisSecond = d3.axisLeft(ySecond);

      doublelineSvgSecondG.append('g')
      .attr('class', 'x axis')
      .attr('transform', 'translate(0, ' + height + ')')
      .call(xAxis)
      .selectAll('text')
      .style('text-anchor', 'end')
      .attr('dx', '-.8em')
      .attr('dy', '.15em')
      .attr('transform', 'rotate(-65)');

      doublelineSvgSecondG.append('g')
      .attr('class', 'y axis')
      .call(yAxisSecond)
      .append('text')
      .attr('transform', 'rotate(-90)')
      .attr('y', 6)
      .attr('dy', '.71em')
      .style('text-anchor', 'end');

      var lineSecond = d3.line()
      .x(function(d)
      return x(d.year);
      )
      .y(function(d)
      return ySecond(d.secondVal);
      )
      .defined(function(d)
      if(isNaN(d.secondVal))
      d.secondVal = 0;

      return d.secondVal !== 0;
      );

      var linesSecond = doublelineSvgSecondG.attr('transform', function(d)
      return 'translate(' + margin.left + ', ' + margin.top + ')';
      );

      console.log('lineSecond', lineSecond);

      linesSecond.append('path')
      .data([dataSmallFilter])
      .attr('class', 'mylines')
      .attr('fill', 'none')
      .attr('stroke', 'tomato')
      .attr('stroke-width', 1)
      .attr('d', lineSecond);









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jun 6 at 13:35









      Gerardo Furtado

      1,1342420




      1,1342420









      asked Jun 6 at 7:13









      Buster

      434




      434




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          3
          down vote



          accepted










          You are very right: deleting the elements and repainting the SVG again is not the correct way to update a D3 chart when the values of a given dataset change. You should frown upon (almost) any D3 code that does this.



          The problem is not only that such approach (which I call a "lazy update") is not idiomatic, the problem is that it doesn't allow the use of the very famous enter-update-exit pattern, and most importantly, it doesn't allow the transition of the elements, which is very important for the user to follow the changes.



          Don't get me wrong, sometimes we do that (hence the "almost" in the first paragraph), but in very specific situations, for very specific reasons.



          Back to your code:



          Unfortunately, the fact that you're deleting the SVGs to repaint them again...



          d3.select('#doublelineDiv').html('');


          ... is not the only big problem here: another major problem is the use of a for loop to append the elements:



          for(var i = 0; i < numCountries; i++) 
          var country = countries[i];
          singleLineChart(country);



          As a rule of thumb, do not use loops (for, while, forEach etc) to append elements in a D3 code. Use proper selections — which, by the way, will fix our first problem.



          Those two advices combined make the required refactor so big that I decided to write an entire new code based on your outcome. This is necessary because refactoring your code will require so many changes that the answer would be a complete mess. However, I'll guide you through the new code step by step.



          First of all, you don't need all that manipulation with the data. You can simply nest it:



          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          That way we'll have an array of three objects, one for each small SVG. Check it:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          Then we create the scales. Since those years are just categorical variables treat them as such, using a point scale. Also, use the same scale for both lines, otherwise you'll be lying to the user:



          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);


          As you can see, there are some magic numbers here, in the final code you should get rid of them.



          Also, we can use the same line generator for both lines (more on that below):



          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );


          And finally we come to the important part, the selections.



          Everything that actually paints the SVGs is inside a function named draw:



          draw("APPLE");

          function draw(fruit)
          //code here



          Inside draw, we'll have all our selections. First, the SVG selection:



          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);


          As you can see, we bind the data to the SVGs themselves, using the countries as a key in the key function.



          Then, we have the paths selection:



          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");


          That data function is complicated, I know. It basically creates two arrays of data for each small SVG, one with firstVal and another one with secondVal. As those arrays have the same properties, we can use a single line generator.



          The nice thing about it is that, now, we can update and transition the paths:



          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );


          Here is the answer to your question: this line...



          .attr("d", function(d) return lineGenerator(d); );


          ... is what makes possible updating the d attribute of the paths without having to delete/repaint all the SVGs again.



          And here is the complete demo:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>





          You may say that the transition is not the most beautiful one, and that's because the interpolator is interpolating strings. You can improve that, for instance, see what Mike Bostock (D3 creator) did here: https://bl.ocks.org/mbostock/3916621. I'll leave that for you, because it's way over the scope of this answer.



          At the end, the code came from 254 lines to 129 lines. But that's not what's important here: what's important here is that now the code is a proper, idiomatic D3 code.




          Edit: According to your comment, you need to use different scales.



          There are several possible solutions here, the easiest one being using the second argument to get the index of the path:



          .attr("d", function(d,i) 
          return i ? lineGenerator2(d) : lineGenerator1(d);
          );


          In the above snippet, lineGenerator1 will be called for the fist path, while lineGenerator2 will be called for the second one. That way, you can set different scales for each line generator.



          However, here is an advice: make that very clear for the end user. Since that the chart has just one y axis (I'm referring to your Plunker, not to your image), almost all users will assume (correctly) that the lines use the same scale.






          share|improve this answer























          • Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
            – Buster
            Jun 6 at 14:22










          • If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
            – Gerardo Furtado
            Jun 7 at 3:07










          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          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: "196"
          ;
          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',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195936%2fupdate-the-line-charts-without-deleting-the-svg%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          3
          down vote



          accepted










          You are very right: deleting the elements and repainting the SVG again is not the correct way to update a D3 chart when the values of a given dataset change. You should frown upon (almost) any D3 code that does this.



          The problem is not only that such approach (which I call a "lazy update") is not idiomatic, the problem is that it doesn't allow the use of the very famous enter-update-exit pattern, and most importantly, it doesn't allow the transition of the elements, which is very important for the user to follow the changes.



          Don't get me wrong, sometimes we do that (hence the "almost" in the first paragraph), but in very specific situations, for very specific reasons.



          Back to your code:



          Unfortunately, the fact that you're deleting the SVGs to repaint them again...



          d3.select('#doublelineDiv').html('');


          ... is not the only big problem here: another major problem is the use of a for loop to append the elements:



          for(var i = 0; i < numCountries; i++) 
          var country = countries[i];
          singleLineChart(country);



          As a rule of thumb, do not use loops (for, while, forEach etc) to append elements in a D3 code. Use proper selections — which, by the way, will fix our first problem.



          Those two advices combined make the required refactor so big that I decided to write an entire new code based on your outcome. This is necessary because refactoring your code will require so many changes that the answer would be a complete mess. However, I'll guide you through the new code step by step.



          First of all, you don't need all that manipulation with the data. You can simply nest it:



          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          That way we'll have an array of three objects, one for each small SVG. Check it:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          Then we create the scales. Since those years are just categorical variables treat them as such, using a point scale. Also, use the same scale for both lines, otherwise you'll be lying to the user:



          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);


          As you can see, there are some magic numbers here, in the final code you should get rid of them.



          Also, we can use the same line generator for both lines (more on that below):



          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );


          And finally we come to the important part, the selections.



          Everything that actually paints the SVGs is inside a function named draw:



          draw("APPLE");

          function draw(fruit)
          //code here



          Inside draw, we'll have all our selections. First, the SVG selection:



          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);


          As you can see, we bind the data to the SVGs themselves, using the countries as a key in the key function.



          Then, we have the paths selection:



          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");


          That data function is complicated, I know. It basically creates two arrays of data for each small SVG, one with firstVal and another one with secondVal. As those arrays have the same properties, we can use a single line generator.



          The nice thing about it is that, now, we can update and transition the paths:



          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );


          Here is the answer to your question: this line...



          .attr("d", function(d) return lineGenerator(d); );


          ... is what makes possible updating the d attribute of the paths without having to delete/repaint all the SVGs again.



          And here is the complete demo:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>





          You may say that the transition is not the most beautiful one, and that's because the interpolator is interpolating strings. You can improve that, for instance, see what Mike Bostock (D3 creator) did here: https://bl.ocks.org/mbostock/3916621. I'll leave that for you, because it's way over the scope of this answer.



          At the end, the code came from 254 lines to 129 lines. But that's not what's important here: what's important here is that now the code is a proper, idiomatic D3 code.




          Edit: According to your comment, you need to use different scales.



          There are several possible solutions here, the easiest one being using the second argument to get the index of the path:



          .attr("d", function(d,i) 
          return i ? lineGenerator2(d) : lineGenerator1(d);
          );


          In the above snippet, lineGenerator1 will be called for the fist path, while lineGenerator2 will be called for the second one. That way, you can set different scales for each line generator.



          However, here is an advice: make that very clear for the end user. Since that the chart has just one y axis (I'm referring to your Plunker, not to your image), almost all users will assume (correctly) that the lines use the same scale.






          share|improve this answer























          • Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
            – Buster
            Jun 6 at 14:22










          • If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
            – Gerardo Furtado
            Jun 7 at 3:07














          up vote
          3
          down vote



          accepted










          You are very right: deleting the elements and repainting the SVG again is not the correct way to update a D3 chart when the values of a given dataset change. You should frown upon (almost) any D3 code that does this.



          The problem is not only that such approach (which I call a "lazy update") is not idiomatic, the problem is that it doesn't allow the use of the very famous enter-update-exit pattern, and most importantly, it doesn't allow the transition of the elements, which is very important for the user to follow the changes.



          Don't get me wrong, sometimes we do that (hence the "almost" in the first paragraph), but in very specific situations, for very specific reasons.



          Back to your code:



          Unfortunately, the fact that you're deleting the SVGs to repaint them again...



          d3.select('#doublelineDiv').html('');


          ... is not the only big problem here: another major problem is the use of a for loop to append the elements:



          for(var i = 0; i < numCountries; i++) 
          var country = countries[i];
          singleLineChart(country);



          As a rule of thumb, do not use loops (for, while, forEach etc) to append elements in a D3 code. Use proper selections — which, by the way, will fix our first problem.



          Those two advices combined make the required refactor so big that I decided to write an entire new code based on your outcome. This is necessary because refactoring your code will require so many changes that the answer would be a complete mess. However, I'll guide you through the new code step by step.



          First of all, you don't need all that manipulation with the data. You can simply nest it:



          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          That way we'll have an array of three objects, one for each small SVG. Check it:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          Then we create the scales. Since those years are just categorical variables treat them as such, using a point scale. Also, use the same scale for both lines, otherwise you'll be lying to the user:



          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);


          As you can see, there are some magic numbers here, in the final code you should get rid of them.



          Also, we can use the same line generator for both lines (more on that below):



          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );


          And finally we come to the important part, the selections.



          Everything that actually paints the SVGs is inside a function named draw:



          draw("APPLE");

          function draw(fruit)
          //code here



          Inside draw, we'll have all our selections. First, the SVG selection:



          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);


          As you can see, we bind the data to the SVGs themselves, using the countries as a key in the key function.



          Then, we have the paths selection:



          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");


          That data function is complicated, I know. It basically creates two arrays of data for each small SVG, one with firstVal and another one with secondVal. As those arrays have the same properties, we can use a single line generator.



          The nice thing about it is that, now, we can update and transition the paths:



          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );


          Here is the answer to your question: this line...



          .attr("d", function(d) return lineGenerator(d); );


          ... is what makes possible updating the d attribute of the paths without having to delete/repaint all the SVGs again.



          And here is the complete demo:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>





          You may say that the transition is not the most beautiful one, and that's because the interpolator is interpolating strings. You can improve that, for instance, see what Mike Bostock (D3 creator) did here: https://bl.ocks.org/mbostock/3916621. I'll leave that for you, because it's way over the scope of this answer.



          At the end, the code came from 254 lines to 129 lines. But that's not what's important here: what's important here is that now the code is a proper, idiomatic D3 code.




          Edit: According to your comment, you need to use different scales.



          There are several possible solutions here, the easiest one being using the second argument to get the index of the path:



          .attr("d", function(d,i) 
          return i ? lineGenerator2(d) : lineGenerator1(d);
          );


          In the above snippet, lineGenerator1 will be called for the fist path, while lineGenerator2 will be called for the second one. That way, you can set different scales for each line generator.



          However, here is an advice: make that very clear for the end user. Since that the chart has just one y axis (I'm referring to your Plunker, not to your image), almost all users will assume (correctly) that the lines use the same scale.






          share|improve this answer























          • Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
            – Buster
            Jun 6 at 14:22










          • If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
            – Gerardo Furtado
            Jun 7 at 3:07












          up vote
          3
          down vote



          accepted







          up vote
          3
          down vote



          accepted






          You are very right: deleting the elements and repainting the SVG again is not the correct way to update a D3 chart when the values of a given dataset change. You should frown upon (almost) any D3 code that does this.



          The problem is not only that such approach (which I call a "lazy update") is not idiomatic, the problem is that it doesn't allow the use of the very famous enter-update-exit pattern, and most importantly, it doesn't allow the transition of the elements, which is very important for the user to follow the changes.



          Don't get me wrong, sometimes we do that (hence the "almost" in the first paragraph), but in very specific situations, for very specific reasons.



          Back to your code:



          Unfortunately, the fact that you're deleting the SVGs to repaint them again...



          d3.select('#doublelineDiv').html('');


          ... is not the only big problem here: another major problem is the use of a for loop to append the elements:



          for(var i = 0; i < numCountries; i++) 
          var country = countries[i];
          singleLineChart(country);



          As a rule of thumb, do not use loops (for, while, forEach etc) to append elements in a D3 code. Use proper selections — which, by the way, will fix our first problem.



          Those two advices combined make the required refactor so big that I decided to write an entire new code based on your outcome. This is necessary because refactoring your code will require so many changes that the answer would be a complete mess. However, I'll guide you through the new code step by step.



          First of all, you don't need all that manipulation with the data. You can simply nest it:



          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          That way we'll have an array of three objects, one for each small SVG. Check it:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          Then we create the scales. Since those years are just categorical variables treat them as such, using a point scale. Also, use the same scale for both lines, otherwise you'll be lying to the user:



          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);


          As you can see, there are some magic numbers here, in the final code you should get rid of them.



          Also, we can use the same line generator for both lines (more on that below):



          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );


          And finally we come to the important part, the selections.



          Everything that actually paints the SVGs is inside a function named draw:



          draw("APPLE");

          function draw(fruit)
          //code here



          Inside draw, we'll have all our selections. First, the SVG selection:



          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);


          As you can see, we bind the data to the SVGs themselves, using the countries as a key in the key function.



          Then, we have the paths selection:



          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");


          That data function is complicated, I know. It basically creates two arrays of data for each small SVG, one with firstVal and another one with secondVal. As those arrays have the same properties, we can use a single line generator.



          The nice thing about it is that, now, we can update and transition the paths:



          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );


          Here is the answer to your question: this line...



          .attr("d", function(d) return lineGenerator(d); );


          ... is what makes possible updating the d attribute of the paths without having to delete/repaint all the SVGs again.



          And here is the complete demo:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>





          You may say that the transition is not the most beautiful one, and that's because the interpolator is interpolating strings. You can improve that, for instance, see what Mike Bostock (D3 creator) did here: https://bl.ocks.org/mbostock/3916621. I'll leave that for you, because it's way over the scope of this answer.



          At the end, the code came from 254 lines to 129 lines. But that's not what's important here: what's important here is that now the code is a proper, idiomatic D3 code.




          Edit: According to your comment, you need to use different scales.



          There are several possible solutions here, the easiest one being using the second argument to get the index of the path:



          .attr("d", function(d,i) 
          return i ? lineGenerator2(d) : lineGenerator1(d);
          );


          In the above snippet, lineGenerator1 will be called for the fist path, while lineGenerator2 will be called for the second one. That way, you can set different scales for each line generator.



          However, here is an advice: make that very clear for the end user. Since that the chart has just one y axis (I'm referring to your Plunker, not to your image), almost all users will assume (correctly) that the lines use the same scale.






          share|improve this answer















          You are very right: deleting the elements and repainting the SVG again is not the correct way to update a D3 chart when the values of a given dataset change. You should frown upon (almost) any D3 code that does this.



          The problem is not only that such approach (which I call a "lazy update") is not idiomatic, the problem is that it doesn't allow the use of the very famous enter-update-exit pattern, and most importantly, it doesn't allow the transition of the elements, which is very important for the user to follow the changes.



          Don't get me wrong, sometimes we do that (hence the "almost" in the first paragraph), but in very specific situations, for very specific reasons.



          Back to your code:



          Unfortunately, the fact that you're deleting the SVGs to repaint them again...



          d3.select('#doublelineDiv').html('');


          ... is not the only big problem here: another major problem is the use of a for loop to append the elements:



          for(var i = 0; i < numCountries; i++) 
          var country = countries[i];
          singleLineChart(country);



          As a rule of thumb, do not use loops (for, while, forEach etc) to append elements in a D3 code. Use proper selections — which, by the way, will fix our first problem.



          Those two advices combined make the required refactor so big that I decided to write an entire new code based on your outcome. This is necessary because refactoring your code will require so many changes that the answer would be a complete mess. However, I'll guide you through the new code step by step.



          First of all, you don't need all that manipulation with the data. You can simply nest it:



          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          That way we'll have an array of three objects, one for each small SVG. Check it:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          Then we create the scales. Since those years are just categorical variables treat them as such, using a point scale. Also, use the same scale for both lines, otherwise you'll be lying to the user:



          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);


          As you can see, there are some magic numbers here, in the final code you should get rid of them.



          Also, we can use the same line generator for both lines (more on that below):



          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );


          And finally we come to the important part, the selections.



          Everything that actually paints the SVGs is inside a function named draw:



          draw("APPLE");

          function draw(fruit)
          //code here



          Inside draw, we'll have all our selections. First, the SVG selection:



          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);


          As you can see, we bind the data to the SVGs themselves, using the countries as a key in the key function.



          Then, we have the paths selection:



          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");


          That data function is complicated, I know. It basically creates two arrays of data for each small SVG, one with firstVal and another one with secondVal. As those arrays have the same properties, we can use a single line generator.



          The nice thing about it is that, now, we can update and transition the paths:



          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );


          Here is the answer to your question: this line...



          .attr("d", function(d) return lineGenerator(d); );


          ... is what makes possible updating the d attribute of the paths without having to delete/repaint all the SVGs again.



          And here is the complete demo:






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>





          You may say that the transition is not the most beautiful one, and that's because the interpolator is interpolating strings. You can improve that, for instance, see what Mike Bostock (D3 creator) did here: https://bl.ocks.org/mbostock/3916621. I'll leave that for you, because it's way over the scope of this answer.



          At the end, the code came from 254 lines to 129 lines. But that's not what's important here: what's important here is that now the code is a proper, idiomatic D3 code.




          Edit: According to your comment, you need to use different scales.



          There are several possible solutions here, the easiest one being using the second argument to get the index of the path:



          .attr("d", function(d,i) 
          return i ? lineGenerator2(d) : lineGenerator1(d);
          );


          In the above snippet, lineGenerator1 will be called for the fist path, while lineGenerator2 will be called for the second one. That way, you can set different scales for each line generator.



          However, here is an advice: make that very clear for the end user. Since that the chart has just one y axis (I'm referring to your Plunker, not to your image), almost all users will assume (correctly) that the lines use the same scale.






          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);

          console.log(nestedData)

          <script src='https://d3js.org/d3.v5.min.js'></script>





          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>





          var data = [
          type: 'APPLE',
          country: 'Germany',
          year: '2000',
          firstVal: '99.5',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2002',
          firstVal: '84',
          secondVal: '2.3'
          ,

          type: 'APPLE',
          country: 'Germany',
          year: '2003',
          firstVal: '86',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2000',
          firstVal: '99',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2001',
          firstVal: '98',
          secondVal: '10'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2002',
          firstVal: '97',
          secondVal: '8'
          ,

          type: 'PEAR',
          country: 'Germany',
          year: '2003',
          firstVal: '98',
          secondVal: '10.9'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2000',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2001',
          firstVal: '85',
          secondVal: '50'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2002',
          firstVal: '86',
          secondVal: '27'
          ,

          type: 'APPLE',
          country: 'France',
          year: '2003',
          firstVal: '86',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2000',
          firstVal: '87',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2001',
          firstVal: '99',
          secondVal: 'NaN'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2002',
          firstVal: '70',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'France',
          year: '2003',
          firstVal: '90',
          secondVal: '9'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2000',
          firstVal: '88',
          secondVal: '14'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2001',
          firstVal: '85',
          secondVal: '2'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2002',
          firstVal: '86',
          secondVal: '1'
          ,

          type: 'APPLE',
          country: 'Spain',
          year: '2003',
          firstVal: '84',
          secondVal: '5'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2000',
          firstVal: '65',
          secondVal: '3'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2001',
          firstVal: 'NaN',
          secondVal: '4'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2002',
          firstVal: '95',
          secondVal: '2'
          ,

          type: 'PEAR',
          country: 'Spain',
          year: '2003',
          firstVal: '78',
          secondVal: '1'

          ];

          var nestedData = d3.nest()
          .key(function(d)
          return d.country
          )
          .entries(data);


          var widthSingleChart = 150;
          var heightSingleChart = 80;
          var vals = ["firstVal", "secondVal"];
          var colors = ["steelblue", "tomato"];
          var xScale = d3.scalePoint()
          .range([20, widthSingleChart - 10])
          .domain([...new Set(data.map(function(d)
          return d.year;
          ))]);
          var yScale = d3.scaleSqrt()
          .range([heightSingleChart - 20, 0])
          .domain([0, d3.max(data, function(d)
          return +d.firstVal;
          )]);
          var lineGenerator = d3.line()
          .x(function(d)
          return xScale(d.year)
          )
          .y(function(d)
          return yScale(d.value)
          )
          .defined(function(d)
          return d.value === d.value
          );
          var xAxis = d3.axisBottom(xScale);
          var yAxis = d3.axisLeft(yScale)
          .ticks(4)

          var div = d3.select("#doublelineDiv");

          draw("APPLE");

          d3.selectAll('input[name=type-selector]').on('change', function()
          draw(this.value)
          )

          function draw(fruit)

          var svgUpdate = div.selectAll("svg")
          .data(nestedData, function(d)
          return d.key
          );

          svgUpdate = svgUpdate.enter()
          .append("svg")
          .attr("width", widthSingleChart)
          .attr("height", heightSingleChart)
          .merge(svgUpdate);

          var paths = svgUpdate.selectAll("path")
          .data(function(d)
          return vals.map(function(e)
          return d.values.filter(function(f)
          return f.type === fruit
          ).map(function(f)
          return
          year: f.year,
          value: +f[e]

          )
          )
          );

          var pathsEnter = paths.enter()
          .append("path")
          .attr("d", function(d)
          return lineGenerator(d);
          )
          .style("stroke", function(_, i)
          return colors[i]
          )
          .style("fill", "none");

          paths.transition()
          .duration(1000)
          .attr("d", function(d)
          return lineGenerator(d);
          );

          var gX = svgUpdate.selectAll(".xaxis")
          .data([1]);

          gX.enter()
          .append("g")
          .attr("class", "xaxis")
          .attr("transform", "translate(0," + (heightSingleChart - 20) + ")")
          .call(xAxis);

          gX.call(xAxis);

          var gY = svgUpdate.selectAll(".yaxis")
          .data([1]);

          gY.enter()
          .append("g")
          .attr("class", "yaxis")
          .attr("transform", "translate(20,0)")
          .call(yAxis);

          gY.call(yAxis);

          <script src='https://d3js.org/d3.v5.min.js'></script>
          <div class='row'>
          <div class='col' id='type-selector-container'>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-apple' name='type-selector' class='custom-control-input' value='APPLE' checked>
          <label class='custom-control-label' for='rb-apple'>APPLE</label>
          </div>
          <div class='custom-control custom-radio custom-control-inline'>
          <input type='radio' id='rb-pear' name='type-selector' class='custom-control-input' value='PEAR'>
          <label class='custom-control-label' for='rb-pear'>PEAR</label>
          </div>
          </div>
          </div>
          <br>
          <div id='doublelineDiv'></div>






          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Jun 7 at 4:51


























          answered Jun 6 at 13:06









          Gerardo Furtado

          1,1342420




          1,1342420











          • Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
            – Buster
            Jun 6 at 14:22










          • If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
            – Gerardo Furtado
            Jun 7 at 3:07
















          • Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
            – Buster
            Jun 6 at 14:22










          • If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
            – Gerardo Furtado
            Jun 7 at 3:07















          Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
          – Buster
          Jun 6 at 14:22




          Thanks Gerardo, you are very kind to explain each step. I'm learning a lot. But I need to use two different scales (no, I'm not lying to users).
          – Buster
          Jun 6 at 14:22












          If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
          – Gerardo Furtado
          Jun 7 at 3:07




          If you need to use two different scales you can change the data function for the paths or simply using the second argument for choosing the scale. I said "lying" because, in a chart like this, where you have just one y axis, you have to explain clearly to the user that the scales are different, otherwise everybody will assume they are the same. Please, check the edit at the end of the answer.
          – Gerardo Furtado
          Jun 7 at 3:07












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f195936%2fupdate-the-line-charts-without-deleting-the-svg%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Greedy Best First Search implementation in Rust

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          C++11 CLH Lock Implementation