Jael B
Jael B

Reputation: 151

How to add legend for a bar chart with different colors in dc.js?

Below is the code snippet for a barchart with colored bars:

    var Dim2 = ndx.dimension(function(d){return [d.SNo, d.something ]});
    var Group2 = Dim2.group().reduceSum(function(d){ return d.someId; });
        
    var someColors = d3.scale.ordinal().domain(["a1","a2","a3","a4","a5","a6","a7","a8"])
.range(["#2980B9","#00FFFF","#008000","#FFC300","#FF5733","#D1AEF1","#C0C0C0","#000000"]);
        
         barChart2
                    .height(250)
                    .width(1000)
                    .brushOn(false)
                    .mouseZoomable(true)
                    .x(d3.scale.linear().domain([600,800]))
                    .elasticY(false)
                    .dimension(Dim2)
                    .group(Group2)
                    .keyAccessor(function(d){ return d.key[0]; })
                    .valueAccessor(function(d){return d.value; })
                    .colors(someColors)
                    .colorAccessor(function(d){return d.key[1]; });

How do I add a legend to this chart?

BarChart:

Upvotes: 2

Views: 1238

Answers (1)

Gordon
Gordon

Reputation: 20150

Using composite keys in crossfilter is really tricky, and I don't recommend it unless you really need it.

Crossfilter only understands scalars, so even though you can produce dimension and group keys which are arrays, and retrieve them correctly, crossfilter is going to coerce those arrays to strings, and that can cause trouble.

Here, what is happening is that Group2.all() iterates over your data in string order, so you get keys in the order

[1, "a1"], [10, "a3"], [11, "a4"], [12, "a5"], [2, "a3"], ...

Without changing the shape of your data, one way around this is to sort the data in your legendables function:

barChart2.legendables = function() { 
    return Group2.all().sort((a,b) => a.key[0] - b.key[0])
        .map(function(kv) { 
            return { 
                chart: barChart2, 
                name: kv.key[1], 
                color: barChart2.colors()(kv.key[1]) }; }) };

An unrelated problem is that dc.js takes the X domain very literally, so even though [1,12] contains all the values, the last bar was not shown because the right side ends right at 12 and the bar is drawn between 12 and 13.

So:

  .x(d3.scale.linear().domain([1,13]))

Now the legend matches the data!

legend matches data[1]

Fork of your fiddle (also with dc.css).

EDIT: Of course, you want the legend items unique, too. You can define uniq like this:

function uniq(a, kf) {
    var seen = [];
    return a.filter(x => seen[kf(x)] ? false : (seen[kf(x)] = true));
}

Adding a step to legendables:

barChart2.legendables = function() { 
      var vals = uniq(Group2.all(), kv => kv.key[1]),
          sorted = vals.sort((a,b) => a.key[1] > b.key[1] ? 1 : -1);
          // or in X order: sorted = vals.sort((a,b) => a.key[0] - b.key[0]);
      return sorted.map(function(kv) { 
                    return { 
                        chart: barChart2, 
                        name: kv.key[1], 
                        color: barChart2.colors()(kv.key[1]) }; }) };

Note that we're sorting by the string value of d.something which lands in key[1]. As shown in the comment, sorting by x order (d.SNo, key[0]) is possible too. I wouldn't recommend sorting by y since that's a reduceSum.

Result, sorted and uniq'd:

sorted and uniq'd

New fiddle.

Upvotes: 1

Related Questions