2
votes

I am looking for how to create a rowchart in dc.js to show and filter items with multiple tags. I've summed up a few answers given on stack overflow, and now have a working code.

var data = [
{id:1, tags: [1,2,3]},
{id:2, tags: [3]},
{id:3, tags: [1]},
{id:4, tags: [2,3]},
{id:5, tags: [3]},
{id:6, tags: [1,2,3]},
{id:7, tags: [1,2]}]; 

    var content=crossfilter(data);

    var idDimension = content.dimension(function (d) { return d.id; });
    var grid = dc.dataTable("#idgrid");
    grid
            .dimension(idDimension)
            .group(function(d){ return "ITEMS" })
            .columns([
                function(d){return d.id+" : "; },
        function(d){return d.tags;},
    ])

    function reduceAdd(p, v) {
        v.tags.forEach (function(val, idx) {
            p[val] = (p[val] || 0) + 1; //increment counts
        });
        return p;
    }

    function reduceRemove(p, v) {
        v.tags.forEach (function(val, idx) {
            p[val] = (p[val] || 0) - 1; //decrement counts
        });
        return p;
    }

    function reduceInitial() {
        return {};
    }


    var tags = content.dimension(function (d) { return d.tags });
    var groupall = tags.groupAll();
    var tagsGroup = groupall.reduce(reduceAdd, reduceRemove, reduceInitial).value();
    tagsGroup.all = function() {
        var newObject = [];
        for (var key in this) {
            if (this.hasOwnProperty(key) && key != "") {
                newObject.push({
                    key: key,
                    value: this[key]
                });
            }
        }
        return newObject;
    }

    var tagsChart = dc.rowChart("#idtags")
    tagsChart
        .width(400)
        .height(200)
        .renderLabel(true)
        .labelOffsetY(10)
        .gap(2)
        .group(tagsGroup)
        .dimension(tags)
        .elasticX(true)
        .transitionDuration(1000)
        .colors(d3.scale.category10())
        .label(function (d) { return d.key })
        .filterHandler (function (dimension, filters) {
            var fm = filters.map(Number)
            dimension.filter(null);
            if (fm.length === 0)
                dimension.filter(null);
            else
                dimension.filterFunction(function (d) {
                    for (var i=0; i < fm.length; i++) {
                        if (d.indexOf(fm[i]) <0) return false;
                    }
                    return true;
                });

            return filters;
        }
    )
    .xAxis().ticks(5);

It can be seen on http://jsfiddle.net/ewm76uru/24/

Nevertheless, the rowchart is not updated when I filter by one tag. For example, on jsfiddle, if you select tag '1', it filters items 1,3,6 and 7. Fine. But the rowchart is not updated... I Should have tag '3' count lowered to 2 for example.

Is there a way to have the rowchart tags counts updated each time I filter by tags ?

Thanks.

1

1 Answers

2
votes

After a long struggle, I think I have finally gathered a working solution.

As said on crossfilter documentation : "a grouping intersects the crossfilter's current filters, except for the associated dimension's filter"

So, the tags dimension is not filtered when tag selection is modified, and there is no flag or function to force this reset. Nevertheless, there is a workaround (given here : https://github.com/square/crossfilter/issues/146).

The idea is to duplicate the 'tags' dimension, and to use it as the filtered dimension :

var tags = content.dimension(function (d) { return d.tags });
// duplicate the dimension
var tags2 = content.dimension(function (d) { return d.tags });
var groupall = tags.groupAll();
...
tagsChart
  .group(tagsGroup)
  .dimension(tags2) // and use this duplicated dimension

as it can been seen here : http://jsfiddle.net/ewm76uru/30/

I hope this will help.