javascript - D3.js: use d3.nest() adding keys dynamically - Stack Overflow

I am using d3.nest() to convert flat JSON files into hierarchical trees. This works just fine if you kn

I am using d3.nest() to convert flat JSON files into hierarchical trees. This works just fine if you know in advance the levels that found in your data: d3.nest() creates a nesting-level for each key function you pass it.

However, I would like to know if there is an elegant way to dynamically pass key functions. This might be an elementary question, but my Javascript skills are also elementary.

This is a static example working with key names stored in an array:

var levels = ['first', 'second', 'third']

var root = {
    "key":"root", 
    "values": 
        d3.nest()
            .key(function(d){ return d[levels[0]] })
            .key(function(d){ return d[levels[1]] })
            .key(function(d){ return d[levels[2]] })
            .entries(data)
}

My solution now is an if-else loop that pseudo-dynamically creates different nestings:

// build the nested tree pseudo-dynamically
if (levels.length === 1) {
    var nest = d3.nest()
           .key(function(d){return d[levels[0]]})
           .entries(data);
} else if (levels.length === 2) {
    var nest = d3.nest()
           .key(function(d){return d[levels[0]]})
           .key(function(d){return d[levels[1]]})
           .entries(data);
} else if (levels.length === 3) {
    var nest = d3.nest()
           .key(function(d){return d[levels[0]]})
           .key(function(d){return d[levels[1]]})
           .key(function(d){return d[levels[2]]})
           .entries(data);
}
[...]

Is there an elegant way to dynamically pass keys to d3.nest()? What I would like is something in principle like the following example (which DOES NOT WORK):

var root = {
    "key":"root", 
    "values": 
        d3.nest()
            for (var i = 0; i < levels.length; i++) {
                .key(function(d){return d[levels[i]]})
            }
            .entries(data)
}

Thanks for your time!

I am using d3.nest() to convert flat JSON files into hierarchical trees. This works just fine if you know in advance the levels that found in your data: d3.nest() creates a nesting-level for each key function you pass it.

However, I would like to know if there is an elegant way to dynamically pass key functions. This might be an elementary question, but my Javascript skills are also elementary.

This is a static example working with key names stored in an array:

var levels = ['first', 'second', 'third']

var root = {
    "key":"root", 
    "values": 
        d3.nest()
            .key(function(d){ return d[levels[0]] })
            .key(function(d){ return d[levels[1]] })
            .key(function(d){ return d[levels[2]] })
            .entries(data)
}

My solution now is an if-else loop that pseudo-dynamically creates different nestings:

// build the nested tree pseudo-dynamically
if (levels.length === 1) {
    var nest = d3.nest()
           .key(function(d){return d[levels[0]]})
           .entries(data);
} else if (levels.length === 2) {
    var nest = d3.nest()
           .key(function(d){return d[levels[0]]})
           .key(function(d){return d[levels[1]]})
           .entries(data);
} else if (levels.length === 3) {
    var nest = d3.nest()
           .key(function(d){return d[levels[0]]})
           .key(function(d){return d[levels[1]]})
           .key(function(d){return d[levels[2]]})
           .entries(data);
}
[...]

Is there an elegant way to dynamically pass keys to d3.nest()? What I would like is something in principle like the following example (which DOES NOT WORK):

var root = {
    "key":"root", 
    "values": 
        d3.nest()
            for (var i = 0; i < levels.length; i++) {
                .key(function(d){return d[levels[i]]})
            }
            .entries(data)
}

Thanks for your time!

Share Improve this question asked Mar 19, 2014 at 16:53 emiguevaraemiguevara 1,3691 gold badge13 silver badges26 bronze badges 1
  • It sounds like nesting isn't the right thing here. This question may help. – Lars Kotthoff Commented Mar 19, 2014 at 19:29
Add a ment  | 

3 Answers 3

Reset to default 5

It's not as easy as you hypothetical elegant code, but you can certainly simplify it pared to your if-else method.

A first instinct is to do something like this:

var levels = ['first', 'second', 'third']

var nest = d3.nest();
for (var i = 0; i < levels.length; i++) {
    nest = nest.key(function(d){return d[levels[i]]});
    //create a new nesting function that has one more key function added
    //and save it in the variable
}

var root = {
      "key":"root", 
      "values": nest.entries(data) //pute the nest
    }

However, this doesn't work because your nesting function won't be used until after you create all your nesting functions, so by the time the nest is actually run your i variable equals 3 and levels[i] returns undefined, and therefore d[levels[i]] returns undefined. Example here.

You need to create a separate function enclosure so that the correct value from levels is locked in:

function createNestingFunction(propertyName){
  return function(d){ 
            return d[propertyName];
         };
}

var levels = ['first', 'second', 'third']

var nest = d3.nest();
for (var i = 0; i < levels.length; i++) {
    nest = nest.key( createNestingFunction(levels[i]) );
    //create a new nesting function that has one more key function added
    //and save it in the variable

    //the function `createNestingFunction` is called *immediately*
    //with a parameter based on the current value of `i`
    //the returned function will always use that parameter,
    //regardless of how many times createNestingFunction is called
}

var root = {
      "key":"root", 
      "values": nest.entries(data) //pute the nest
    }

The working version of the example: http://fiddle.jshell/brVLH/1/

Found another possible solution after posting the question. It uses forEach and it is described here.

A simpler solution, following AmeliaBR one:

let levels = ['first', 'second', 'third']
// create the nest
let nest = d3.nest()
// for each value in 'levels' array
// add the key() function
levels.forEach(function(level){
  // re-assign to the nest key
  nest = nest.key(d => d[level])
});
// pute the nest
nest = nest.entries(data)
// add the root
let root = {
  "key":"root", 
  "values": nest
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742335655a4424595.html

相关推荐

  • javascript - D3.js: use d3.nest() adding keys dynamically - Stack Overflow

    I am using d3.nest() to convert flat JSON files into hierarchical trees. This works just fine if you kn

    6小时前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信