3

How can I limit the depth of recursion in the function?

3 Answers 3

8

Here's a simple function that inserts '...' when an object has been seen before, preventing infinite recursion.

  function safeStringify (value) {
    const seen = new Set()
    return JSON.stringify(value, (k, v) => {
      if (seen.has(v)) { return '...' }
      if (typeof v === 'object') { seen.add(v) }
      return v
    })
  }
Sign up to request clarification or add additional context in comments.

Comments

3

When creating a recursive function, you can always pass a so called accumulative parameter. Simply pass an extra numeric parameter that you increment each time you enter a new level of recursion (i.e. increment your parameter exactly once each recursive iteration) and pass it through.

You can use this technique for a variety of things, as well as keeping track of the depth of recursion. Simply check the value of the parameter at the start of your function, and return when it is equal to the maximum depth that you wish to traverse into.

Example

This example shows a variable called depthLevel that signifies the level of depth in a tree for the current node. The maxDepthLevel constant should be defined somewhere. This Depth First algorithm will not traverse deeper than maxDepthLevel. Notice how the depthLevel is increased by 1 for each level of recursion, making it an accumulative parameter.

function depthFirst(var node, var depthLevel) {
    if(depthLevel > maxDepthLevel) {
        return;
    }

    //do logic for this node here

    var childrenOfThisNode = node.getChildren();

    foreach(var child in childrenOfThisNode) {
        depthFirst(child, depthLevel + 1)
    }
}

2 Comments

can you write an example?
@OliverWatkins there you go
0
const a = {
  a: 3,
  b: {
    b1: "sg",
    b2: "sif",
    b3: {
      bc1: 3,
      bc2: null,
    },
  },
  c: {
    c1: 6,
    c2: false,
  },
};

a.b.b3.bc3 = a;

const p = document.querySelector("p");

class StringifyTracker {
  topLevelValue = null;
  seend = new WeakSet();
  seenr = new WeakSet();
  maxDepth = 0;

  constructor(maxDepth) {
    if (maxDepth != null) {
      this.maxDepth = maxDepth;
    }
  }

  depthWithin(obj, subobj, level, found) {
    let ret = {
      level,
      found: found || obj === subobj,
    };
    if (!ret.found && !this.seend.has(this.topLevelValue)) {
      for (const key in obj) {
        if (
          !found &&
          obj[key] &&
          typeof obj[key] == "object" &&
          !this.seend.has(obj[key])
        ) {
          this.seend.add(obj[key]);
          const dw = this.depthWithin(obj[key], subobj, level + 1, found);
          if (dw.found) {
            ret = dw;
            break;
          }
        }
      }
    }
    return ret;
  }

  replacer(key, value) {
    let ret = value;
    if (!this.topLevelValue) {
      this.topLevelValue = value;
    }
    if (!(value === null || value === undefined) && typeof value === "object") {
      if (this.seenr.has(value)) {
        ret = "[Circular Ref]";
      } else {
        this.seend = new WeakSet();
        const depth = this.depthWithin(
          this.topLevelValue,
          value,
          0,
          false
        ).level;
        console.log("depth of " + key + " = " + depth);
        if (depth > this.maxDepth) {
          ret = "[Object]";
        }
        this.seenr.add(value);
      }
    }
    return ret;
  }
}

const st = new StringifyTracker(1);
p.innerHTML = JSON.stringify(a, (key, value) => st.replacer(key, value), 4)
  .replaceAll("\n", "</p><p>")
  .replaceAll(" ", "&nbsp");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.