title: d3 - how to format xAxis ticks depending on weeks
body: <p>I have a d3 time scale chart. At the moment, the axis ticks render a date for every data object. The data could have a range of anything from 1 day of data, or 2 weeks in 1 month, or 5 months worth of data or even more, for example.</p>
  
  <p>Ideally, we want to display ticks with a week number, <em>based on the data</em> - <strong>not</strong> the week number of the year or month like this: <code>xAxis.tickFormat(d3.time.format('%W'))</code> Eg, if the data starts from 15th of July, the first tick would say 'week 1', then 'week 2', etc.</p>
  
  <p>How could you achieve such axis ticks with a time based d3 line chart? I'm new to d3 and almost lost with how to achieve this. </p>
  
  <p>I'm using moment.js as well, so inside <code>xAxis.tickFormat</code>, i've tried using a function with some logic that returns different values depending on the date, but this seems fragile and not 'the d3 way'. Also tried using a custom time formatter <a href="http://bl.ocks.org/mbostock/4149176" rel="nofollow">as seen here</a>.</p>
  
  <p>Alternatively, we could have simpler ticks - just displaying the month and/or day with <code>d3.time.format(%d-%b)</code>, but then there are duplicate tick values like 'Feb...Feb..Feb..Feb...Feb..Mar..Mar..Mar..`. Is there a method to prevent duplicate values from appearing?</p>
  
  <p>I've tried limiting the amount of ticks, but this isn't working as expected. Eg, If I have <code>xAxis.ticks(5)</code>, 3 ticks appear; same story if I have <code>xAxis.ticks(2)</code>. If ticks is defined as 1, only 1 tick appears. What's going on here?</p>
  
  <p>Any help would be most appreciated! Code below with 2 dataset examples</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  
  &lt;body&gt;
  
  &lt;style&gt;
  path { fill: #CCC; }
  .line { fill: none; stroke: #000; stroke-width: 5px;}
  &lt;/style&gt;
  
  &lt;div id="chart-container"&gt;
      &lt;svg id="chart"&gt;&lt;/svg&gt;
  &lt;/div&gt;
  
  &lt;script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js"&gt;&lt;/script&gt;
  &lt;script src="//d3js.org/d3.v3.min.js"&gt;&lt;/script&gt;
  
  &lt;script&gt;
  
      var now = moment();
      var chartData = [
          { timestamp: moment(now).subtract(27, 'days').format('DD-MMM-YY'), value: 40 },
          { timestamp: moment(now).subtract(25, 'days').format('DD-MMM-YY'), value: 36 },
          { timestamp: moment(now).subtract(24, 'days').format('DD-MMM-YY'), value: 33 },
          { timestamp: moment(now).subtract(21, 'days').format('DD-MMM-YY'), value: 35 },
          { timestamp: moment(now).subtract(20, 'days').format('DD-MMM-YY'), value: 35 },
          { timestamp: moment(now).subtract(18, 'days').format('DD-MMM-YY'), value: 33 },
          { timestamp: moment(now).subtract(17, 'days').format('DD-MMM-YY'), value: 33 },
          { timestamp: moment(now).subtract(16, 'days').format('DD-MMM-YY'), value: 33 },
          { timestamp: moment(now).subtract(15, 'days').format('DD-MMM-YY'), value: 32 },
          { timestamp: moment(now).subtract(13, 'days').format('DD-MMM-YY'), value: 35 },
          { timestamp: moment(now).subtract(11, 'days').format('DD-MMM-YY'), value: 31 },
          { timestamp: moment(now).subtract(10, 'days').format('DD-MMM-YY'), value: 28 },
          { timestamp: moment(now).subtract(9, 'days').format('DD-MMM-YY'), value: 32 },
          { timestamp: moment(now).subtract(8, 'days').format('DD-MMM-YY'), value: 30 },
          { timestamp: moment(now).subtract(7, 'days').format('DD-MMM-YY'), value: 33 },
          { timestamp: moment(now).subtract(6, 'days').format('DD-MMM-YY'), value: 36 }
      ];
  
  
      //data could have a shorter date range of eg, 1 or 2 weeks
      //ideally we want to still display 'week 1, 2, 3, 4' etc in the axis.
      //alternatively display dates instead
      // var chartData = [
      //     { timestamp: moment(now).subtract(27, 'days').format('DD-MMM-YY'), value: 40 },
      //     { timestamp: moment(now).subtract(25, 'days').format('DD-MMM-YY'), value: 36 },
      //     { timestamp: moment(now).subtract(24, 'days').format('DD-MMM-YY'), value: 33 },
      //     { timestamp: moment(now).subtract(21, 'days').format('DD-MMM-YY'), value: 35 },
      //     { timestamp: moment(now).subtract(20, 'days').format('DD-MMM-YY'), value: 35 }
      // ];
  
      let lastObj = chartData[chartData.length - 1];
      let lastObjTimestamp =  lastObj.timestamp;
      let lastAndNow = moment(lastObjTimestamp).diff(now, 'days');
      console.log('difference between last entry ' + lastObjTimestamp  + ' and today: ' + lastAndNow);
  
      var chartWrapperDomId = 'chart-container';
      var chartDomId = 'chart';
      var chartWrapperWidth = document.getElementById(chartWrapperDomId).clientWidth;
      var margin = 40;
      var width = chartWrapperWidth - margin;
      var height = 500 - margin * 2;
  
      var xMin = d3.time.format('%d-%b-%y').parse(chartData[0].timestamp);
      var xMax = d3.time.format('%d-%b-%y').parse(chartData[chartData.length-1].timestamp);
  
      //set the scale for the x axis
      var xScale = d3.time.scale();
      xScale.domain([xMin, xMax]);
      xScale.range([0, width]);
  
      var yScale = d3.scale.linear()
          .range([height, 0])
          .nice();
  
      console.log('no5 ', chartData[5].timestamp)
  
      var xAxis = d3.svg.axis()
          .scale(xScale)
          .orient('bottom')
          .tickFormat(d3.time.format('%d-%b'));
          //.tickFormat(d3.time.format('%b'))
          //tickFormat(d3.time.format('%W'));
          //.ticks(5);
  
      var yAxis = d3.svg.axis()
          .scale(yScale)
          .orient('left');
  
      var line = d3.svg.line()
          .x(function(d) {
              return xScale(d.timestamp);
          })
          .y(function(d) {
              return yScale(d.value);
          });
  
      var svg = d3.select('#' + chartDomId)
          .attr('width', width + margin * 2)
          .attr('height', height + margin * 2)
        .append('g')
          .attr('transform', 'translate(' + margin + ',' + margin + ')');
  
      chartData.forEach(function(d) {
          d.timestamp = d3.time.format('%d-%b-%y').parse(d.timestamp);
          d.value = +d.value;
      });
  
      yScale.domain(d3.extent(chartData, function(d) {
          return d.value;
      }));
  
      svg.append('g')
          .attr('class', 'axis x-axis')
          .attr('transform', 'translate(0,' + height + ')')
          .call(xAxis);
  
      svg.append('g')
          .attr('class', 'axis y-axis')
          .call(yAxis)
      .append('text')
          .attr('transform', 'rotate(-90)')
          .attr('y', 6)
          .attr('dy', '.71em')
          .style('text-anchor', 'end');
  
      svg.append('path')
          .datum(chartData)
          .attr('class', 'line')
          .attr('d', line);
  
  &lt;/script&gt;
  
  &lt;/body&gt;</code></pre>
  </div>
  </div>
  </p>