【问题标题】:transform flat json object to hierarchical d3 object structure将平面 json 对象转换为分层 d3 对象结构
【发布时间】:2021-11-12 13:08:36
【问题描述】:

我想将像packageJson这样的json数据转换成适合树形图的分层格式

使用d3.hierarchy 转换对象 据我了解 每个包含嵌套数据的属性 需要使用内部的辅助函数进行转换

const test = d3.hierarchy(packageJson, d => d.scripts);

是通过查找每个具有嵌套对象的属性来转换packageJson 的唯一方法
或者是我不知道的任何 d3 辅助函数?

const fam = d3.hierarchy({
    name: "root",
    children: [
      {name: "child #1"},
      {
        name: "child #2",
        children: [
          {name: "grandchild #1"},
          {name: "grandchild #2"},
          {name: "grandchild #3"}
        ]
      }
    ]
  })


// transform json 

const packageJson = {
  "name": "visualize-data",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "d3-color": "^3.0.1",
    "d3-hierarchy": "^3.0.1",
    "d3-selection": "^3.0.0",
    "d3-shape": "^3.0.1",
    "vue": "^3.0.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^7.0.0"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/vue3-essential",
      "eslint:recommended"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}



const test = d3.hierarchy(packageJson, d => d.scripts);

console.log(test)
    #limit {
        max-width: 100px;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>



 <div id="limit">

    </div>

【问题讨论】:

    标签: javascript json d3.js data-structures


    【解决方案1】:

    最简单的方法是创建一个包含所有子属性的数组作为 d3.hierarchy 使用的子数组:

    let root = d3.hierarchy(packageJson, function(d) {
      if(typeof d == "object")
       return Object.keys(d).map(k=>{ return d[k]; }); 
    })
    

    我们需要检查该属性是否是一个对象(/数组)而不是文本,否则我们将提取标记叶节点的字符串的键。

    这不是最令人满意的结果:没有节点有名字。我们可以通过根据父节点给它们的属性名称来给节点命名来解决这个问题(根仍然是无名的,但这很容易纠正):

    let root = d3.hierarchy(packageJson, function(d) {
      if(typeof d == "object")
       return Object.keys(d).filter(d=>d!="$name").map(k=>{
         if(typeof d[k] == "object") d[k].$name = k;
         else d[k] = k + " : " + d[k];
         return d[k];
       }); 
    })
    

    $name 属性跟踪非叶节点的名称(因为这些是我们可以添加属性的对象),它被子数组生成器过滤掉。由于叶节点是字符串,我只是修改它们以反映属性名称和值:它们的值位于 d.data 而非叶节点的名称位于 d.data.$name。

    根据您想要的结果,您可能喜欢也可能不喜欢上述方法。但为了演示,如下图所示:

    这是一个演示:

    const packageJson = {
      "name": "visualize-data",
      "version": "0.1.0",
      "private": true,
      "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "lint": "vue-cli-service lint"
      },
      "dependencies": {
        "core-js": "^3.6.5",
        "d3-color": "^3.0.1",
        "d3-hierarchy": "^3.0.1",
        "d3-selection": "^3.0.0",
        "d3-shape": "^3.0.1",
        "vue": "^3.0.0"
      },
      "devDependencies": {
        "@vue/cli-plugin-babel": "~4.5.0",
        "@vue/cli-plugin-eslint": "~4.5.0",
        "@vue/cli-service": "~4.5.0",
        "@vue/compiler-sfc": "^3.0.0",
        "babel-eslint": "^10.1.0",
        "eslint": "^6.7.2",
        "eslint-plugin-vue": "^7.0.0"
      },
      "eslintConfig": {
        "root": true,
        "env": {
          "node": true
        },
        "extends": [
          "plugin:vue/vue3-essential",
          "eslint:recommended"
        ],
        "parserOptions": {
          "parser": "babel-eslint"
        },
        "rules": {}
      },
      "browserslist": [
        "> 1%",
        "last 2 versions",
        "not dead"
      ]
    }
    
    let root = d3.hierarchy(packageJson, function(d) {
      if(typeof d == "object")
       return Object.keys(d).filter(d=>d!="$name").map(k=>{
         if(typeof d[k] == "object") d[k].$name = k;
         else d[k] = k + " : " + d[k];
         return d[k];
       }); 
    })
    
    
    var width = 600;
    var height = 300;
    
    margin = {left: 10, top: 10, right: 50, bottom: 50}
    
    var svg = d3.select("body").append("svg")
          .attr("width", width)
          .attr("height", height);
          
    var g = svg.append("g").attr('transform','translate('+ margin.left +','+ margin.right +')');
    
    var tree = d3.tree()
        .size([height-margin.top-margin.bottom,width-margin.left-margin.right]);
    
     var link = g.selectAll(".link")
        .data(tree(root).links())
        .enter().append("path")
          .attr("class", "link")
          .attr("d", d3.linkHorizontal()
              .x(function(d) { return d.y; })
              .y(function(d) { return d.x; }));
    
      var node = g.selectAll(".node")
        .data(root.descendants())
        .enter().append("g")
          .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
          .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
    
      node.append("circle")
          .attr("r", 2.5);
          
      node.append("text")
         .text(function(d) { return d.data.$name || d.data; })
         .attr('y',-10)
         .attr('x',-10)
         .attr('text-anchor','middle');
    .node circle {
              fill: #fff;
              stroke: steelblue;
              stroke-width: 3px;
            }
    
            .link {
              fill: none;
              stroke: #ccc;
              stroke-width: 2px;
            }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"&gt;&lt;/script&gt;

    【讨论】:

    • 优秀的解决方案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多