source: node_modules/d3-hierarchy/src/stratify.js@ e4c61dd

Last change on this file since e4c61dd was e4c61dd, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Prototype 1.1

  • Property mode set to 100644
File size: 4.0 KB
Line 
1import {optional} from "./accessors.js";
2import {Node, computeHeight} from "./hierarchy/index.js";
3
4var preroot = {depth: -1},
5 ambiguous = {},
6 imputed = {};
7
8function defaultId(d) {
9 return d.id;
10}
11
12function defaultParentId(d) {
13 return d.parentId;
14}
15
16export default function() {
17 var id = defaultId,
18 parentId = defaultParentId,
19 path;
20
21 function stratify(data) {
22 var nodes = Array.from(data),
23 currentId = id,
24 currentParentId = parentId,
25 n,
26 d,
27 i,
28 root,
29 parent,
30 node,
31 nodeId,
32 nodeKey,
33 nodeByKey = new Map;
34
35 if (path != null) {
36 const I = nodes.map((d, i) => normalize(path(d, i, data)));
37 const P = I.map(parentof);
38 const S = new Set(I).add("");
39 for (const i of P) {
40 if (!S.has(i)) {
41 S.add(i);
42 I.push(i);
43 P.push(parentof(i));
44 nodes.push(imputed);
45 }
46 }
47 currentId = (_, i) => I[i];
48 currentParentId = (_, i) => P[i];
49 }
50
51 for (i = 0, n = nodes.length; i < n; ++i) {
52 d = nodes[i], node = nodes[i] = new Node(d);
53 if ((nodeId = currentId(d, i, data)) != null && (nodeId += "")) {
54 nodeKey = node.id = nodeId;
55 nodeByKey.set(nodeKey, nodeByKey.has(nodeKey) ? ambiguous : node);
56 }
57 if ((nodeId = currentParentId(d, i, data)) != null && (nodeId += "")) {
58 node.parent = nodeId;
59 }
60 }
61
62 for (i = 0; i < n; ++i) {
63 node = nodes[i];
64 if (nodeId = node.parent) {
65 parent = nodeByKey.get(nodeId);
66 if (!parent) throw new Error("missing: " + nodeId);
67 if (parent === ambiguous) throw new Error("ambiguous: " + nodeId);
68 if (parent.children) parent.children.push(node);
69 else parent.children = [node];
70 node.parent = parent;
71 } else {
72 if (root) throw new Error("multiple roots");
73 root = node;
74 }
75 }
76
77 if (!root) throw new Error("no root");
78
79 // When imputing internal nodes, only introduce roots if needed.
80 // Then replace the imputed marker data with null.
81 if (path != null) {
82 while (root.data === imputed && root.children.length === 1) {
83 root = root.children[0], --n;
84 }
85 for (let i = nodes.length - 1; i >= 0; --i) {
86 node = nodes[i];
87 if (node.data !== imputed) break;
88 node.data = null;
89 }
90 }
91
92 root.parent = preroot;
93 root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight);
94 root.parent = null;
95 if (n > 0) throw new Error("cycle");
96
97 return root;
98 }
99
100 stratify.id = function(x) {
101 return arguments.length ? (id = optional(x), stratify) : id;
102 };
103
104 stratify.parentId = function(x) {
105 return arguments.length ? (parentId = optional(x), stratify) : parentId;
106 };
107
108 stratify.path = function(x) {
109 return arguments.length ? (path = optional(x), stratify) : path;
110 };
111
112 return stratify;
113}
114
115// To normalize a path, we coerce to a string, strip the trailing slash if any
116// (as long as the trailing slash is not immediately preceded by another slash),
117// and add leading slash if missing.
118function normalize(path) {
119 path = `${path}`;
120 let i = path.length;
121 if (slash(path, i - 1) && !slash(path, i - 2)) path = path.slice(0, -1);
122 return path[0] === "/" ? path : `/${path}`;
123}
124
125// Walk backwards to find the first slash that is not the leading slash, e.g.:
126// "/foo/bar" ⇥ "/foo", "/foo" ⇥ "/", "/" ↦ "". (The root is special-cased
127// because the id of the root must be a truthy value.)
128function parentof(path) {
129 let i = path.length;
130 if (i < 2) return "";
131 while (--i > 1) if (slash(path, i)) break;
132 return path.slice(0, i);
133}
134
135// Slashes can be escaped; to determine whether a slash is a path delimiter, we
136// count the number of preceding backslashes escaping the forward slash: an odd
137// number indicates an escaped forward slash.
138function slash(path, i) {
139 if (path[i] === "/") {
140 let k = 0;
141 while (i > 0 && path[--i] === "\\") ++k;
142 if ((k & 1) === 0) return true;
143 }
144 return false;
145}
Note: See TracBrowser for help on using the repository browser.