d3-hierarchy icon indicating copy to clipboard operation
d3-hierarchy copied to clipboard

New nodesAtDepth(depth) function

Open benjaminpreiss opened this issue 4 years ago • 3 comments

Hey Mike

Great work. For tree traversal it would be great to have a function that returns all nodes at a certain depth. Example (taken from source code for leaves()):

export default function(depth) {
  var nodes = [];
  this.eachBefore(function(node) {
    if (node.depth === depth) {
      nodes.push(node);
    }
  });
  return nodes;
}

kind regards Benjamin

benjaminpreiss avatar May 07 '21 15:05 benjaminpreiss

We should probably cut short when we're at the desired depth, and avoid a traversal of the branches that are deeper.

Fil avatar May 07 '21 17:05 Fil

@Fil Yeah, true... Thinking about this, it would be useful to extend iterator.js as well as create another function called eachToDepth:

First, the changes to iterator.js:

export default function*(depth = this.height) {
  var node = this, current, next = [node], children, i, n;
  do {
    current = next.reverse(), next = [];
    while (node = current.pop()) {
      yield node;
      if ((children = node.children) && node.depth < depth) {
        for (i = 0, n = children.length; i < n; ++i) {
          next.push(children[i]);
        }
      }
    }
  } while (next.length);
}

Now the new eachToDepth.js file (leveraging our changed iterator):

export default function(callback, depth, that) {
  let index = -1;
  for (const node of this[Symbol.iterator](depth)) {
    callback.call(that, node, ++index, this);
  }
  return this;
}

Then we can use the changes above to construct nodesAtDepth.js:

export default function(depth) {
  const nodes = []
  this.eachToDepth(function(node) {
    if (node.depth === depth) {
      nodes.push(node);
    }
  }, depth);
  return nodes
}

The above proposals include nodes at the specified depth (traverse tree breadth first including descendants with specified depth)

benjaminpreiss avatar May 08 '21 10:05 benjaminpreiss

Or maybe we don't even need eachToDepth.js...

In that case nodesAtDepth.js would be:

export default function(depth) {
  const nodes = []
  for (const node of this[Symbol.iterator](depth)) {
    if (node.depth === depth) {
      nodes.push(node);
    }
  }
  return nodes
}

benjaminpreiss avatar May 08 '21 11:05 benjaminpreiss