D3.js:使用d3.nest()动态添加键

时间:2014-03-19 16:53:59

标签: javascript json d3.js

我使用d3.nest()将平面JSON文件转换为分层树。如果您事先知道数据中的级别,则可以正常工作:d3.nest()为您传递的每个键函数创建嵌套级别。

但是,我想知道是否有一种优雅的方式来动态传递关键功能。这可能是一个基本问题,但我的Javascript技能也是基本的。

这是一个使用存储在数组中的键名的静态示例:

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)
}

我的解决方案现在是一个if-else循环,伪动态地创建不同的嵌套:

// 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);
}
[...]

是否有一种优雅的方式可以将密钥动态传递给d3.nest()?我想要的是原则上的东西,如下面的例子(它不工作):

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

谢谢你的时间!

3 个答案:

答案 0 :(得分:4)

它并不像假设的优雅代码那么容易,但与if-else方法相比,你当然可以简化它。

第一直觉是做这样的事情:

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) //compute the nest
    }

然而,这不起作用,因为在您创建所有嵌套函数之前,您的嵌套函数不会被使用,所以到时候实际上,您的i变量等于3并且levels[i]返回undefined,因此d[levels[i]]返回undefined。 Example here

您需要创建一个单独的功能附件,以便锁定levels中的正确值:

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) //compute the nest
    }

示例的工作版本:http://fiddle.jshell.net/brVLH/1/

答案 1 :(得分:1)

在发布问题后找到另一种可能的解决方案。它使用forEach并描述here

答案 2 :(得分:1)

遵循AmeliaBR一个更简单的解决方案:

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])
});
// compute the nest
nest = nest.entries(data)
// add the root
let root = {
  "key":"root", 
  "values": nest
}