call method

HierarchyNode<T?> call(
  1. Iterable<T> data
)

Generates a new hierarchy from the specified tabular data.

final root = stratify(data);

Implementation

HierarchyNode<T?> call(Iterable<T> data) {
  var data0 = <Object?>[...data],
      nodes = <HierarchyNode<Object?>>[],
      nodeByKey = <String, HierarchyNode<Object?>>{};
  Function? currentId = id, currentParentId = parentId;
  int i, n;
  Object? d;
  HierarchyNode<Object?>? root;
  HierarchyNode<Object?>? parent;
  HierarchyNode<Object?> node;
  Object? nodeId;
  String nodeKey;

  if (path != null) {
    i = 0;
    final I = data.map((d) => normalize(path!(d, i++, data))).toList();
    final P = I.map(parentof).toList();
    final S = I.toSet()..add("");
    i = 0;
    String pi;
    while (i < P.length) {
      pi = P[i];
      if (!S.contains(pi)) {
        S.add(pi);
        I.add(pi);
        P.add(parentof(pi));
        data0.add(imputed);
      }
      i++;
    }
    currentId = (_, i, __) => I[i];
    currentParentId = (_, i, __) => P[i];
  }

  i = 0;
  n = data0.length;
  for (; i < n; ++i) {
    d = data0[i];
    nodes.add(node = HierarchyNode(d));
    //if (d == imputed) continue;
    if ((nodeId = currentId?.call(d, i, data)?.toString()) != null &&
        (nodeId as String).isNotEmpty) {
      nodeKey = node.id = nodeId;
      nodeByKey[nodeKey] = nodeByKey.containsKey(nodeKey) ? ambiguous : node;
    }
    if ((nodeId = currentParentId?.call(d, i, data)?.toString()) != null &&
        (nodeId as String).isNotEmpty) {
      node.parentId = nodeId;
    }
  }

  for (i = 0; i < n; ++i) {
    node = nodes[i];
    if ((nodeId = node.parentId) != null) {
      parent = nodeByKey[nodeId];
      if (parent == null) throw ArgumentError("missing: $nodeId");
      if (parent == ambiguous) throw ArgumentError("ambiguous: $nodeId");
      if (parent.children != null) {
        parent.children!.add(node);
      } else {
        parent.children = [node];
      }
      node.parent = parent;
    } else {
      if (root != null) throw ArgumentError("multiple roots");
      root = node;
    }
  }

  if (root == null) throw ArgumentError("no root");

  // When imputing internal nodes, only introduce roots if needed.
  // Then replace the imputed marker data with null.
  if (path != null) {
    while (root!.data == imputed && root.children!.length == 1) {
      root = root.children![0];
      --n;
    }
    for (var i = nodes.length - 1; i >= 0; --i) {
      node = nodes[i];
      if (node.data != imputed) break;
      node.data = null;
    }
  }

  root.parent = preroot;
  (root
        ..eachBefore((node, _, __, [___]) {
          node.depth = node.parent!.depth + 1;
          --n;
        }))
      .eachBefore(computeHeight);
  root.parent = null;
  if (n > 0) throw ArgumentError("cycle");

  return cast<Object?, T?>(root);
}