1

I'm using a d3.js Streamgraph to show sales over time. Then I've got a transition to show profit over the same period. The data looks like this:

var data = [{
    "name": "apples",
    "sales": [{
        "x": 0,
        "y": 941
    }, {
        "x": 1,
        "y": 490
    }],
    "profit": [{
        "x": 0,
        "y": 6
    }, {
        "x": 1,
        "y": 3
    }]
}, {
    "name": "oranges",
    "sales": [{
        "x": 0,
        "y": 344
    }, {
        "x": 1,
        "y": 425
    }],
    "profit": [{
        "x": 0,
        "y": 3
    }, {
        "x": 1,
        "y": 2
    }]
}];

It works, but I'm currently generating the stacked data in a somewhat ham-fisted way, applying stack.values twice:

var stack_sales = d3.layout.stack()
    .offset("wiggle")
    .values(function(d) { return d.sales; });

var stack_profit = d3.layout.stack()
    .offset("wiggle")
    .values(function(d) { return d.profit; });

stack_sales(data);
stack_profit(data);

I've been getting into JavaScript Patterns but can't see the DRY way to do this. Can you help?

4

1 回答 1

2

One option is to define the values function dynamically:

var stack = d3.layout.stack().offset("wiggle");
["sales", "profit"].forEach(function(metric) {
  stack.values(function(d) { return d[metric]; })(data);
});

Another option would be to transpose your data so that the metrics are on the outside and the series are on the inside:

var metrics = [
  {
    "name": "sales",
    "series": [
      {
        "name": "apples",
        "values": [
          {"x": 0, "y": 941}, 
          {"x": 1, "y": 490}
        ]
      }, 
      {
        "name": "oranges",
        "values": [
          {"x": 0, "y": 344}, 
          {"x": 1, "y": 425}
        ]
      }
    ]
  }, {
    "name": "profits",
    "series": [
      {
        "name": "apples",
        "values": [
          {"x": 0, "y": 0}, 
          {"x": 1, "y": 6}
        ]
      }, 
      {
        "name": "oranges",
        "values": [
          {"x": 0, "y": 1}, 
          {"x": 1, "y": 3}
        ]
      }
    ]
  }
];

Then you can access the values statically:

var stack = d3.layout.stack()
    .offset("wiggle")
    .values(function(d) { return d.values; });

metrics.forEach(function(metric) {
  stack(metric.series);
});
于 2012-08-19T17:49:47.657 回答