Arash Howaida
Arash Howaida

Reputation: 2617

D3.js X axis as index number

I have a relatively straight forward question: How can I have x be defined as the index number that is returned from the tsv file I'm parsing using data.map and/or d3.tsv()?

Consider the following code:

d3.tsv("data.tsv", function(error, data) {
  if (error) throw error;

  data.forEach(function(d) {
    d.x = +d.x;
    d.y = +d.y;
  });

I need to adapt this code slightly, because my .tsv actually only has 1 column of data, the y values. Instead of going into the .tsv and adding a new column that counts from 1 to n, is there someway of just defining my x values to be whatever the index of +d.y is?

I tried using google, but the word 'index' is so general, I just got a bunch of documentation that didn't get to what I was after.

Hopefully it's a quick fix, let me know if you got a way,

Thank you

Upvotes: 0

Views: 1309

Answers (1)

Aurelio
Aurelio

Reputation: 25792

If I get what you need you have two methods for doing it. You either attach a new field to the data returned from the AJAX call:

d3.tsv("data.tsv", function(error, data) {
  if (error) throw error;

  var myData = data.map(function(item, i) {
    item.x = i + 1;
    return item;
  })

This will hardcode the position of each item as it is returned from the request.

In many cases though this is not needed, as looping over data inside a selection in D3 already gives you access to the index. For example:

// later in your code

svg
    .selectAll('circle')
    .data(myData)
    .enter()
    .append('circle')
    .attr('cx', function(d, i) {
       // here the 2 arguments are:
       // d => the data you passed in 
       // i => is the index of the loop, starting from 0
       return i + 1;
    })

EDIT

In both cases you will loop over the array, so the performance hit is there anyway (but unless your dataset is REALLY huge I would not worry at all).

More than a difference in performance, it's a difference in your expected usage.

As said, the first method stores the position of each item in the dataset and will always reflect this position even after you reoder the array. Example, given this csv data:

name age
John 24
Tom 32
Alan 22

The first method will create this object:

[{
    name: "John",
    age: 24,
    x: 1
},
{
    name: "Tom",
    age: 32,
    x: 2
},
{
    name: "Alan",
    age: 22,
    x: 3
}]

Maybe later you want to reorder this array from younger to older:

[{
    name: "Alan",
    age: 22,
    x: 3
}, {
    name: "John",
    age: 24,
    x: 1
},
{
    name: "Tom",
    age: 32,
    x: 2
}]

Notice how the x value is unchanged. If that's what you want, use this method.

If you instead use the second method, the i in this function

.attr('cx', function(d, i) {
   // i => is the index of the loop, starting from 0
   return i + 1;
})

will always reflect the position of that data in that particular array. So passing this

[{
    name: "John",
    age: 24
},
{
    name: "Tom",
    age: 32
},
{
    name: "Alan",
    age: 22
}]

will result in

.attr('cx', function(d, i) {
   // i === 0 => John
   // i === 1 => Tom
   // i === 2 => Alan
   return i + 1;
})

While passing this:

[{
    name: "Alan",
    age: 22
}, {
    name: "John",
    age: 24
},
{
    name: "Tom",
    age: 32
}]

will result in:

.attr('cx', function(d, i) {
   // i === 0 => Alan
   // i === 1 => John
   // i === 2 => Tom
   return i + 1;
})

Upvotes: 4

Related Questions