diff --git a/README.md b/README.md
index 94296d15..71362fe9 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,11 @@ A reference to the data associated with this node, as specified to the [construc
# node.depth
-The depth of the node: zero for the root node, and increasing by one for each subsequent generation.
+The depth of the node: zero for the root node, and increasing by one for each descendant generation.
+
+# node.height
+
+The height of the node: zero for any leaf node, and increasing by one for each ancestor generation.
# node.parent
@@ -140,7 +144,6 @@ Similarly, to sort nodes by descending height (greatest distance from any descen
```js
root
.sum(function(d) { return d.value; })
- .eachBefore(function(d) { var h = 0; do d.height = h; while ((d = d.parent) && (d.height < ++h)); })
.sort(function(a, b) { return b.height - a.height || b.value - a.value; });
```
@@ -495,6 +498,7 @@ This returns:
{
"id": "Eve",
"depth": 0,
+ "height": 2,
"data": {
"id": "Eve",
"parentId": ""
@@ -503,6 +507,7 @@ This returns:
{
"id": "Cain",
"depth": 1,
+ "height": 0,
"parent": [Circular],
"data": {
"id": "Cain",
@@ -512,6 +517,7 @@ This returns:
{
"id": "Seth",
"depth": 1,
+ "height": 1,
"parent": [Circular],
"data": {
"id": "Seth",
@@ -521,6 +527,7 @@ This returns:
{
"id": "Enos",
"depth": 2,
+ "height": 0,
"parent": [Circular],
"data": {
"id": "Enos",
@@ -530,6 +537,7 @@ This returns:
{
"id": "Noam",
"depth": 2,
+ "height": 0,
"parent": [Circular],
"data": {
"id": "Noam",
@@ -541,6 +549,7 @@ This returns:
{
"id": "Abel",
"depth": 1,
+ "height": 0,
"parent": [Circular],
"data": {
"id": "Abel",
@@ -550,6 +559,7 @@ This returns:
{
"id": "Awan",
"depth": 1,
+ "height": 1,
"parent": [Circular],
"data": {
"id": "Awan",
@@ -559,6 +569,7 @@ This returns:
{
"id": "Enoch",
"depth": 2,
+ "height": 0,
"parent": [Circular],
"data": {
"id": "Enoch",
@@ -570,6 +581,7 @@ This returns:
{
"id": "Azura",
"depth": 1,
+ "height": 0,
"parent": [Circular],
"data": {
"id": "Azura",
diff --git a/src/hierarchy/index.js b/src/hierarchy/index.js
index 5d560856..f7c0edfb 100644
--- a/src/hierarchy/index.js
+++ b/src/hierarchy/index.js
@@ -30,18 +30,27 @@ export default function hierarchy(data) {
}
}
- return root;
+ return root.eachBefore(computeHeight);
}
function node_copy() {
- return hierarchy(this).eachBefore(function(node) {
- node.data = node.data.data;
- });
+ return hierarchy(this).eachBefore(copyData);
+}
+
+function copyData(node) {
+ node.data = node.data.data;
+}
+
+export function computeHeight(node) {
+ var height = 0;
+ do node.height = height;
+ while ((node = node.parent) && (node.height < ++height));
}
export function Node(data) {
this.data = data;
- this.depth = 0;
+ this.depth =
+ this.height = 0;
this.parent = null;
}
diff --git a/src/stratify.js b/src/stratify.js
index 10db8b2b..88e48d13 100644
--- a/src/stratify.js
+++ b/src/stratify.js
@@ -1,5 +1,5 @@
import {required} from "./accessors";
-import {Node} from "./hierarchy/index";
+import {Node, computeHeight} from "./hierarchy/index";
var keyPrefix = "$", // Protect against keys like “__proto__”.
preroot = {depth: -1};
@@ -53,7 +53,7 @@ export default function() {
if (!root) throw new Error("no root");
root.parent = preroot;
- root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; });
+ root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight);
root.parent = null;
if (n > 0) throw new Error("cycle");
diff --git a/test/stratify-test.js b/test/stratify-test.js
index b7481f1a..e1daaf83 100644
--- a/test/stratify-test.js
+++ b/test/stratify-test.js
@@ -20,16 +20,19 @@ tape("stratify(data) returns the root node", function(test) {
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 2,
data: {id: "a"},
children: [
{
id: "aa",
depth: 1,
+ height: 1,
data: {id: "aa", parentId: "a"},
children: [
{
id: "aaa",
depth: 2,
+ height: 0,
data: {id: "aaa", parentId: "aa"}
}
]
@@ -37,6 +40,7 @@ tape("stratify(data) returns the root node", function(test) {
{
id: "ab",
depth: 1,
+ height: 0,
data: {id: "ab", parentId: "a"}
}
]
@@ -55,16 +59,19 @@ tape("stratify(data) does not require the data to be in topological order", func
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 2,
data: {id: "a"},
children: [
{
id: "aa",
depth: 1,
+ height: 1,
data: {id: "aa", parentId: "a"},
children: [
{
id: "aaa",
depth: 2,
+ height: 0,
data: {id: "aaa", parentId: "aa"}
}
]
@@ -72,6 +79,7 @@ tape("stratify(data) does not require the data to be in topological order", func
{
id: "ab",
depth: 1,
+ height: 0,
data: {id: "ab", parentId: "a"}
}
]
@@ -90,21 +98,25 @@ tape("stratify(data) preserves the input order of siblings", function(test) {
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 2,
data: {id: "a"},
children: [
{
id: "ab",
depth: 1,
+ height: 0,
data: {id: "ab", parentId: "a"}
},
{
id: "aa",
depth: 1,
+ height: 1,
data: {id: "aa", parentId: "a"},
children: [
{
id: "aaa",
depth: 2,
+ height: 0,
data: {id: "aaa", parentId: "aa"}
}
]
@@ -125,16 +137,19 @@ tape("stratify(data) treats an empty parentId as the root", function(test) {
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 2,
data: {id: "a", parentId: ""},
children: [
{
id: "aa",
depth: 1,
+ height: 1,
data: {id: "aa", parentId: "a"},
children: [
{
id: "aaa",
depth: 2,
+ height: 0,
data: {id: "aaa", parentId: "aa"}
}
]
@@ -142,6 +157,7 @@ tape("stratify(data) treats an empty parentId as the root", function(test) {
{
id: "ab",
depth: 1,
+ height: 0,
data: {id: "ab", parentId: "a"}
}
]
@@ -159,16 +175,19 @@ tape("stratify(data) does not treat a falsy but non-empty parentId as the root",
test.deepEqual(noparent(root), {
id: "0",
depth: 0,
+ height: 1,
data: {id: 0, parentId: null},
children: [
{
id: "1",
depth: 1,
+ height: 0,
data: {id: 1, parentId: 0}
},
{
id: "2",
depth: 1,
+ height: 0,
data: {id: 2, parentId: 0}
}
]
@@ -213,14 +232,17 @@ tape("stratify(data) allows the id to be undefined for leaf nodes", function(tes
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 1,
data: {id: "a"},
children: [
{
depth: 1,
+ height: 0,
data: {parentId: "a"}
},
{
depth: 1,
+ height: 0,
data: {parentId: "a"}
}
]
@@ -245,10 +267,12 @@ tape("stratify(data) allows the id to be undefined for leaf nodes", function(tes
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 1,
data: {id: "a"},
children: [
{
depth: 1,
+ height: 0,
data: o
}
]
@@ -269,16 +293,19 @@ tape("stratify.id(id) observes the specified id function", function(test) {
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 2,
data: {foo: "a"},
children: [
{
id: "aa",
depth: 1,
+ height: 1,
data: {foo: "aa", parentId: "a"},
children: [
{
id: "aaa",
depth: 2,
+ height: 0,
data: {foo: "aaa", parentId:"aa" }
}
]
@@ -286,6 +313,7 @@ tape("stratify.id(id) observes the specified id function", function(test) {
{
id: "ab",
depth: 1,
+ height: 0,
data: {foo: "ab", parentId:"a" }
}
]
@@ -313,16 +341,19 @@ tape("stratify.parentId(id) observes the specified parent id function", function
test.deepEqual(noparent(root), {
id: "a",
depth: 0,
+ height: 2,
data: {id: "a"},
children: [
{
id: "aa",
depth: 1,
+ height: 1,
data: {id: "aa", foo: "a"},
children: [
{
id: "aaa",
depth: 2,
+ height: 0,
data: {id: "aaa", foo: "aa"}
}
]
@@ -330,6 +361,7 @@ tape("stratify.parentId(id) observes the specified parent id function", function
{
id: "ab",
depth: 1,
+ height: 0,
data: {id: "ab", foo: "a"}
}
]
diff --git a/test/treemap/flare-test.js b/test/treemap/flare-test.js
index 1d71b749..02dd62b3 100644
--- a/test/treemap/flare-test.js
+++ b/test/treemap/flare-test.js
@@ -50,6 +50,7 @@ function test(input, expected, tile) {
delete node.parent;
delete node.data;
delete node._squarify;
+ delete node.height;
if (node.children) node.children.forEach(visit);
})(actual);