@msamblanet/deep-iterator

Deep iteration of any object or iterable collection. (This version revised by @msablanet)

Usage no npm install needed!

<script type="module">
  import msamblanetDeepIterator from 'https://cdn.skypack.dev/@msamblanet/deep-iterator';
</script>

README

deep-iterator

npm version License: MIT

NOTE: This is not the original branch for deep-iterator. This is a modified branch maintained by @msamblanet to incoroprate typescript definitions, convert into an ESM module, and bring dependencies up to date.

deep-iterator iterates recursively through objects, arrays, sets, maps, generic iterables, or any mix of them.

Features

  • several search algorithms: bfs, dfs pre/post order
  • circular references detection
  • can iterate either all the nodes or just the leaves
  • can skip iteration of specified objects

Installation

npm install deep-iterator

Usage

import deepIterator from 'deep-iterator';
// commonJS : var deepIterator = require('deep-iterator').default;

const obj = {a: 1, b: [2, 3]};
for (let {value} of deepIterator(obj)) {
  console.log(value);
}
/* outputs:
{a: 1, b: [2, 3]}  (the root) - use the onlyLeaves option to remove
1
[2, 3] - use the onlyLeaves option to remove
2
3
*/

const deepArray = [[1, 2], [3, 4]];
const it = deepIterator(deepArray, {onlyLeaves: true});
for (let {parent, key} of it) {
  parent[key]++;
}
// deepArray ===  [[2, 3], [4, 5]]

The node object

For each iteration, deep-iterator yields a node object containing the following members :

  • value: the value of the node
  • parent: the parent of the node
  • key: the key of the node in its parent
  • path: an array containing all the keys from the root down to the node
  • parentNode: the parent node object
  • isCircular(): true if the node is a circular reference
  • isLeaf(): true if the node is a leaf
  • type: a string representing the type of the node. Possible values are:
    • Null,
    • Undefined,
    • Boolean,
    • String,
    • Symbol,
    • Date,
    • RegExp,
    • Function,
    • GeneratorFunction,
    • Promise,
    • Array,
    • Set,
    • Map,
    • UserDefinedIterable: an iterable that is not an array, map, set or string,
    • NonIterableObject: an object that doesn't implement the iterator protocol

Additionally, the node object contains helpers methods to determine its type, in the form of "isType()".
Examples : isMap(), isNonIterableObject(), ...

Remarks on the node object :

  • parent[key] lets you update the current node
  • key is undefined if the node is the root or the iterated collection is a user defined iterable
  • path and type are lazy evaluated : not using them will improve performance, i. e. :
for (let {value, parent, key} of it) => path is not evaluated
for (let {value, parent, key, path} of it) => path is evaluated

Options

Default options are :

const iterator = deepIterator(obj, {
  search: 'dfsPreOrder', // DFS algorithm / parent before child
  onlyLeaves: false, // all nodes are iterated
  circularReference: 'leaf', // circular references are treated as leaves (not recursively iterated)
  iterateOverObject: true, // non-iterable objects are iterated through their properties
  skipIteration : node => false // no node are skipped
});

search

accepted values: 'dfsPreOrder'(default), 'dfsPostOrder', 'bfs'

Specifies the iteration algorithm:

  • dfsPreOrder: Depth First Search, parent before child
  • dfsPostOrder: Depth First Search, child before parent
  • bfs: Breadth First Search

onlyLeaves

accepted values: false (default) or true

Iterates all the nodes or only leaves

circularReference

accepted values: 'leaf', 'throw', 'noCheck'

Handles the circular references detection :

  • leaf: circular references are treated as leaves (no further iteration)
  • throw: circular references throw an error
  • noCheck: circular detection is off (this improves performances)

iterateOverObject

accepted values: true(default) or false

Enables iteration over non-iterable objects or not.

skipIteration

accepted values: callback: node => boolean

Specifies whether a recursive iteration is performed on the node or not.

  • if the callback returns true : the node is not iterated itself and is considered as a leaf
  • if the callback returns false : the node is deeply iterated

Note: skipIteration is not called on leaves.

Example : Skip iteration of all arrays :

const it = deepIterator(obj, {skipIteration: node => Array.isArray(node.value)});