Simple D3 code for drawing area charts I am quite new to D3 and JavaScript in general, so I'm sure there is a way to write the following in a lot less code:

function CalculatePeriodReturns(){
    periodReturns = [];
    [...]
    periodReturns.push({
        length: [...],
        mean: [...],
        pctile50: [pctile(0.25),pctile(0.75)],
        pctile90: [pctile(0.05),pctile(0.95)],
        pctile99: [pctile(0.005),pctile(0.995)],
        min: [...],
        max: [...]
    });
}

function DrawChart(){
    var margin = {top: 20, right: 100, bottom: 30, left: 100},
        width = $("#multiperiod_chart").width() - margin.left - margin.right,
        height = $("#multiperiod_chart").height() - margin.top - margin.bottom;

    var svg = d3.select("#multiperiod_chart").append("svg")
        .datum(periodReturns)
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .attr("id", "multiperiod_chart_svg")
        .append("g")
        .attr("transform",
              "translate(" + margin.left + "," + margin.top + ")");

    var x = d3.scaleLinear().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    x.domain(d3.extent(periodReturns, d=&gt;d.length));
    y.domain([d3.min(periodReturns, d =&gt; d.min), 
        d3.max(periodReturns, d =&gt; d.max)]); 

    var area99 = d3.area()
        .x(d=&gt;x(d.length))
        .y0(d=&gt;y(d.pctile99[0]))      
        .y1(d=&gt;y(d.pctile99[1]))    

    svg.append("path")
        .attr("class", "area")
        .attr("id", "area99")
        .attr("fill", "#565A68")
        .attr("d", area99);

    var area90 = d3.area()
        .x(d=&gt;x(d.length))
        .y0(d=&gt;y(d.pctile90[0]))      
        .y1(d=&gt;y(d.pctile90[1]))    

    svg.append("path")
        .attr("class", "area")
        .attr("id", "area90")
        .attr("fill", "#7B7F8B")
        .attr("d", area90);

    var area50 = d3.area()
        .x(d=&gt;x(d.length))
        .y0(d=&gt;y(d.pctile50[0]))      
        .y1(d=&gt;y(d.pctile50[1]))    

    svg.append("path")
        .attr("class", "area")
        .attr("id", "area50")
        .attr("fill", "#A1A3AA")
        .attr("d", area50);

    [...]

}


For a start, is there a way to create an accessor area(y0,y1) that takes the two d.pctileXX properties as attributes or similar? Maybe I can combine the three svg.append's as well?
You can simplify your area generators, as well as your code to append the paths, using an each to append the paths or an object to define the x and y methods in the area generator.

However, there is an simpler alternative: you are facing all those difficulties because of the structure of your data. If you change your data's structure for a more adequate one you can create a code which is way more idiomatic, therefore easier to read and, the most important, easier to maintain.

Right now, this is your data structure:

var periodReturns = [{
    pectile99: [x, y],
    pectile90: [x, y],
    pectile50: [x, y]
}, {
    pectile99: [x, y],
    pectile90: [x, y],
    pectile50: [x, y]
}, {
    etc...
}];


You will bend over backwards to create a multiarea chart with a data structure like this, because:


The data() methods accepts a data array in which each object is a datum;
You cannot easily get the objects' keys;


So, let's change that data structure. We'll created a nested data, with a key/value pair for the name, conveniently called name, and a key/value pair (containing an array) for the points, conveniently called values:



var data = [{
  pectile99: [0, 10],
  pectile90: [0, 40],
  pectile50: [0, 100]
}, {
  pectile99: [100, 40],
  pectile90: [100, 140],
  pectile50: [100, 30]
}, {
  pectile99: [200, 110],
  pectile90: [200, 10],
  pectile50: [200, 100]
}, {
  pectile99: [250, 90],
  pectile90: [250, 70],
  pectile50: [250, 140]
}, {
  pectile99: [300, 10],
  pectile90: [300, 30],
  pectile50: [300, 150]
}];

var nestedData = Object.keys(data[0]).map(function(d) {
  var obj = {
    name: d,
    values: []
  };
  for (var i = 0; i &lt; data.length; i++) {
    obj.values.push(data[i][d])
  };
  return obj;
});

console.log(nestedData)




Now, since all the points can be accessed using the same property name, we can easily write just one area generator. Also, since each object in nestedData corresponds to just one area, we can write a single enter selection!

var areaGenerator = d3.area()
    .x(function(d) {
        return d[0]
    })
    .y1(function(d) {
        return d[1]
    })
    .y0(150);

var areas = svg.selectAll(null)
    .data(nestedData)
    .enter()
    .append("path")
    .attr("fill", function(d, i) {
        return color(i)
    })
    .attr("opacity", 0.5)
    .attr("d", function(d) {
        return areaGenerator(d.values);
    })


Here is the demo:



var data = [{
  pectile99: [0, 10],
  pectile90: [0, 40],
  pectile50: [0, 100]
}, {
  pectile99: [100, 40],
  pectile90: [100, 140],
  pectile50: [100, 30]
}, {
  pectile99: [200, 110],
  pectile90: [200, 10],
  pectile50: [200, 100]
}, {
  pectile99: [250, 90],
  pectile90: [250, 70],
  pectile50: [250, 140]
}, {
  pectile99: [300, 10],
  pectile90: [300, 30],
  pectile50: [300, 150]
}];

var svg = d3.select("svg");

var color = d3.scaleOrdinal(d3.schemeCategory10)

var prop;

var nestedData = Object.keys(data[0]).map(function(d) {
  var obj = {
    name: d,
    values: []
  };
  for (var i = 0; i &lt; data.length; i++) {
    obj.values.push(data[i][d])
  };
  return obj;
});

var areaGenerator = d3.area()
  .x(function(d) {
    return d[0]
  })
  .y1(function(d) {
    return d[1]
  })
  .y0(150);

var areas = svg.selectAll(null)
  .data(nestedData)
  .enter()
  .append("path")
  .attr("fill", function(d, i) {
    return color(i)
  })
  .attr("opacity", 0.5)
  .attr("d", function(d) {
    return areaGenerator(d.values);
  })
&lt;script src="https://d3js.org/d3.v4.min.js"&gt;&lt;/script&gt;
&lt;svg&gt;&lt;/svg&gt;