【问题标题】:D3.js: use d3.nest() adding keys dynamicallyD3.js:使用 d3.nest() 动态添加键
【发布时间】:2014-03-19 16:53:58
【问题描述】:

我正在使用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)
}

感谢您的宝贵时间!

【问题讨论】:

  • 听起来嵌套在这里不是正确的事情。 This question 可能会有所帮助。

标签: javascript json d3.js


【解决方案1】:

它不像你假设的优雅代码那么容易,但与你的 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] 返回未定义,因此 d[levels[i]] 返回未定义。 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/

【讨论】:

  • 感谢您提供这么好的答案!效果很好,实际上非常优雅!
  • 如果我需要根据分组来识别级别怎么办?假设我有两个 group by 作为键,我怎么知道这个级别是基于这个值的?不知何故,我可以将我用来分组的 attr 添加到 key attr,如 attkey 等?
  • @AmitRana 我不太清楚你的意思。也许开始一个新问题,以便您可以更完整地解释它(通过简单的示例)并且其他人可以尝试提供帮助?
  • @AmeliaBR,谢谢,非常有用。我更喜欢使用 forEach 而不是 for 循环,因此您不需要单独的函数:levels.forEach(function (k) { nest = nest.key(function (d) { return d[k] }); });
【解决方案2】:

发布问题后找到另一个可能的解决方案。它使用 forEach,它被描述为here

【讨论】:

  • 不添加多个键不是问题...我想要的当我添加多个键时,它会根据这些键对它们进行分组...对于每个级别,我只想要哪个级别按哪个分组键...
  • 我真的不明白你的评论,@Amit
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 2020-07-27
    • 2012-12-21
    • 1970-01-01
    • 2022-10-17
    相关资源
    最近更新 更多