Transform flat table to hierarchy

Viewed 39

I want to transform a table from a csv into a hierarchy. The table has the following format:

lvl1 lvl2 lvl3 size
   A    a    1    1
   A    a    2    2
   A    b    1    3
   A    b    2    4
   B    a    1    5
   B    a    2    6
   B    b    1    7
   B    b    2    8

Which translates to the JSON object below.

I used d3.nest like this:

var n = d3.nest()
  .key(d => d.lvl1)
  .key(d => d.lvl2)
  .key(d => d.lvl3)
  .entries(d)

which results in the following format which is almost what I want to pass to d3.hierarchy:

[{
    "key": "A",
    "values": [{
      "key": "a",
      "values": [{
          "key": "1",
          "values": [{
            "lvl1": "A",
            "lvl2": "a",
            "lvl3": 1,
            "size": 1
          }]
        },
        {
          "key": "2",
          "values": [{
            "lvl1": "A",
            "lvl2": "a",
            "lvl3": 2,
            "size": 2
          }]
        }
      ]
    }]
  }
  //...
]

However, this adds one level too many and ideally I would like to have the following output:

[{
    "key": "A",
    "values": [{
      "key": "a",
      "values": [{
          "key": "1",
          "size": 1
        },
        {
          "key": "2",
          "size": 2
        }
      ]
    }]
  } //,...
]

This format I could conveniently pass down to d3.hierarchy(n, d => d.values). How to achieve that?

N.B. I don't care if there are additional fields in the leaf, thus, an output like this would also be fine as long as the leaf is not nested again:

[{
    "key": "A",
    "values": [{
      "key": "a",
      "values": [{
          "key": "1",
          "lvl1": "A",
          "lvl2": "a",
          "lvl3": 1,
          "size": 1
        },
        {
          "key": "2",
          "lvl1": "A",
          "lvl2": "a",
          "lvl3": 2,
          "size": 2

        }
      ]
    }]
  } //,...
]

JSON

[{
    "lvl1": "A",
    "lvl2": "a",
    "lvl3": 1,
    "size": 1
  },
  {
    "lvl1": "A",
    "lvl2": "a",
    "lvl3": 2,
    "size": 2
  },
  {
    "lvl1": "A",
    "lvl2": "b",
    "lvl3": 1,
    "size": 3
  },
  {
    "lvl1": "A",
    "lvl2": "b",
    "lvl3": 2,
    "size": 4
  },
  {
    "lvl1": "B",
    "lvl2": "a",
    "lvl3": 1,
    "size": 5
  },
  {
    "lvl1": "B",
    "lvl2": "a",
    "lvl3": 2,
    "size": 6
  },
  {
    "lvl1": "B",
    "lvl2": "b",
    "lvl3": 1,
    "size": 7
  },
  {
    "lvl1": "B",
    "lvl2": "b",
    "lvl3": 2,
    "size": 8
  }
]
1 Answers

You can get what you want with a rollup:

const n = d3.nest()
  .key(d => d.lvl1)
  .key(d => d.lvl2)
  .rollup(d => d.map(e => ({
    key: e.lvl3,
    size: e.size
  })))
  .entries(data);

Here is the demo:

const data = [{
    "lvl1": "A",
    "lvl2": "a",
    "lvl3": 1,
    "size": 1
  },
  {
    "lvl1": "A",
    "lvl2": "a",
    "lvl3": 2,
    "size": 2
  },
  {
    "lvl1": "A",
    "lvl2": "b",
    "lvl3": 1,
    "size": 3
  },
  {
    "lvl1": "A",
    "lvl2": "b",
    "lvl3": 2,
    "size": 4
  },
  {
    "lvl1": "B",
    "lvl2": "a",
    "lvl3": 1,
    "size": 5
  },
  {
    "lvl1": "B",
    "lvl2": "a",
    "lvl3": 2,
    "size": 6
  },
  {
    "lvl1": "B",
    "lvl2": "b",
    "lvl3": 1,
    "size": 7
  },
  {
    "lvl1": "B",
    "lvl2": "b",
    "lvl3": 2,
    "size": 8
  }
];

var nested = d3.nest()
  .key(d => d.lvl1)
  .key(d => d.lvl2)
  .rollup(d => d.map(e => ({
    key: e.lvl3,
    size: e.size
  })))
  .entries(data);

console.log(nested);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Related