d3 adding data attribute conditionally

Seems like a good candidate for .each():

var th = d3.select(selection).select("thead").selectAll("th")
        .data(colspec)
    .enter().append("th")
        .text(function(d) { return d["data-name"]; })
        // now address each item individually
        .each(function(d) {
            var header = d3.select(this);
            // loop through the keys - this assumes no extra data
            d3.keys(d).forEach(function(key) {
                if (key != "data-name")
                    header.attr(key, d[key]);
            });
        });

I often use .each when having a per-item scope makes more sense than trying to figure out a bunch of attributes for each item.

For a short list of attributes, especially if you're worried about extra data in the objects, it's probably easier to loop through the desired keys instead of everything:

        .each(function(d) {
            var header = d3.select(this);
            ['data-class', 'data-hide', 'data-ignore'].forEach(function(key) {
                if (key in d)
                    header.attr(key, d[key]);
            });
        });

You don't need to call each() or filter()... The attr() function will do this for you internally. Just call it with a function instead of a value, and have that function return the desired value for each datum, or null if the attribute is not desired for a particular datum, like so:

...
.attr('data-class', function(d) {
    return 'data-class' in d ? d['data-class'] : null;
});

If your function returns null, the attribute is not added. You can even combine several attributes into one call by providing a map of attr names to functions like so:

...
.attr({
    'data-class': function(d) {
        return 'data-class' in d ? d['data-class'] : null;
    }, 
    'data-hide': function(d) {
        return 'data-hide' in d ? d['data-hide'] : null;
    },
    'data-ignore': function(d) {
        return 'data-ignore' in d ? d['data-ignore'] : null;
    }
});

or if you're like me and would rather not type so much, you can reduce the list of attribute names into the appropriate map:

...
.attr(['data-class', 'data-hide', 'data-ignore'].reduce(function(result, attr) {
    result[attr] = function(d) {
        return attr in d ? d[attr] : null;
    }
    return result;
}, {}));

Tags:

D3.Js