【问题标题】:How do I write recursive d3.js code to deal with nested data structures?如何编写递归 d3.js 代码来处理嵌套数据结构?
【发布时间】:2013-01-17 13:48:51
【问题描述】:

我有functional programming 的背景,原则上理解递归,但我似乎无法将这些知识转化为D3.js 环境。

我在下面有一个 hello world 脚本,它试图简单地打印嵌套数据结构的内容。根据其他线程的建议,我可以使用.filter 仅返回节点,但是如何继续这个示例以递归方式打印嵌套项?

<!DOCYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="d3.v3.js"></script>

        <script>
            function draw(data)
            {
                "use strict";

                d3.select("body")
                    .selectAll("p")
                    .data(data)
                    .enter()
                    .append("p")
                    .text(function(d) {
                            if (d instanceof Array) {
                                return "WHAT DO I PUT HERE?";
                            }
                            else {
                                return d;
                            };
                        });
            }
        </script>
    </head>

    <body>
        Hello world

        <script>
            draw([1, [2, [1, 2, 3, 4] ], 3, 4, 5]);
        </script>
    </body>
</html>

【问题讨论】:

标签: javascript recursion d3.js nested


【解决方案1】:

你需要一个根特征,然后是一个递归函数来填充它。

function makeNestedListItems (parentLists) {
    var item = parentLists.append('li')
        .text(function (d) { return d.txt; });
    var children = parentLists.selectAll('ul')
        .data(function (d) {
            return d.children
        })
      .enter().append('ul');
    if (!children.empty()) {
        makeNestedListItems(children);
    }
}
var data = {
    txt: 'root',
    children: [{
            txt: "a",
            children: [{
                    txt: "aa",
                    children: [{
                            txt: "aaa",
                            children: []
                        }, {
                            txt: "aab",
                            children: []
                        }
                    ]
                }, {
                    txt: "ab",
                    children: []
                }
            ]
        }, {
            txt: "b",
            children: [{
                    txt: "ba",
                    children: []
                }, {
                    txt: "bb",
                    children: []
                }, {
                    txt: "bc",
                    children: []
                }
            ]
        }, {
            txt: "c",
            children: []
        }
    ]
};
var rootList = d3.select('body').selectAll('ul').data([data])
        .enter().append('ul');
makeNestedListItems(rootList);

应该产生什么

    • 一个
      • aa
        • aaa
        • aab
      • ab
    • b
      • bb
      • 公元前
    • c

【讨论】:

    【解决方案2】:

    做到这一点的简单方法是避免递归!典型的 D3.js 方法是递归数据并确定布局所需的信息(例如,子项的总大小、嵌套的总深度、每个节点的深度),然后展平结构并使用计算的值进行布局.

    可以在this tree example 中找到一个很好的示例,其中使用内置函数处理计算和展平:

    var tree = d3.layout.tree()...
    

    也就是说,如果您真的想尝试直接在布局中进行递归所需的选择体操,您可以。关键是你必须做出选择,然后根据父母的数据设置他们的数据。

    在下面的示例中,为了方便起见,我对 maxLevels 进行了硬编码,但您可以在进入循环之前根据数据计算它。

    另外请注意,我对布局非常懒惰,因为要正确地做到这一点,您需要首先对数据进行递归传递,以便在开始之前至少计算每个元素有多少个子元素。你可以玩小提琴here

    var data = { children: [{
            txt: "a", children: [{
                txt: "aa", children: [{
                    txt: "aaa"}, {
                    txt: "aab"}]}, {
                txt: "ab"}]}, {
            txt: "b", children: [{
                txt: "ba"}, {
                txt: "bb"}, {
                txt: "bc"}]}, {
            txt: "c"}]};
    
    var svg = d3.selectAll("svg");
    
    svg.attr({ width: 500, height: 500});
    
    var recurse = svg.selectAll("g.level0").data([data]).enter()
        .append("g").classed("level0", true);
    
    var maxLevels = 4;
    for (var level = 0; level < maxLevels; level++) {
    
        var nextLevel = level + 1;
    
        var next = svg.selectAll("g.level" + level).filter(function (d) {
            return d.children !== undefined;
        });
    
        next.selectAll("g.level" + nextLevel)
            .data(function (d) { return d.children; })
            .enter().append("g")
            .attr("class", function (d) {
                return "level" + nextLevel + " " + d.txt;
            })
            .attr("transform", function (d, i) {
                return "translate(" + (nextLevel * 25) + "," + (i * 10 * (5 - level) + 15) + ")";
            });
    
        next.selectAll("text.level" + nextLevel)
            .data(function (d) {  return d.children; })
            .enter().append("text")
            .classed("level" + level, true)
            .attr("x", function (d, i, j) {  return nextLevel * 25;  })
            .attr("y", function (d, i, j) {
                return j * (10 * (10 - level)) + (i+1) * 15;
            })
            .attr("fill", "black")
            .text(function (d) { return d.txt; });
    }
    

    【讨论】:

      猜你喜欢
      • 2013-11-23
      • 1970-01-01
      • 2023-03-14
      • 2011-08-08
      • 2011-09-08
      • 2012-04-22
      • 2017-12-12
      • 2016-07-12
      • 1970-01-01
      相关资源
      最近更新 更多