Skip to content
Advertisement

Is there a way to traverse a possibly-self-containing object in JavaScript?

I want to descend an object in Javascript looking for a specific string. Unfortunately, this object is built in such a way that it’d be impossible to simply use the source and Ctrl-F for that string, and it’s also built in such a way that recursive functions trying to descend it risk getting trapped inside of it forever.

Basically, this object contains itself. Not just once, but in very many areas. I cannot simply say “exclude these keys”, as the object is obfuscated and therefore we’d be here all day listing keys, and once we were done we wouldn’t have looked at all the data.
As well, I need to be able to descend __proto__ and prototype, as useful strings are hidden in there too. (But only for functions and objects.)

While I’d prefer something along the lines of findStuff(object, /string/ig), that may be hard, so any function that simply has areas clearly marked that the control flow falls to once it’s found specific objects (function, string, etc.)

Thank you, and sorry for such a pain in the butt question.


Edit: In case it helps, I’m trying to traverse a compiled Construct2 runtime object. I’m not going to post the full thing here as it’s not going to fit in any pastebin no matter how forgiving, and also I don’t want to accidentally post resources I don’t have the permission to provide. (Don’t worry though, I’m not trying to pirate it myself, I’m simply trying to figure out some user-facing functionality)

Advertisement

Answer

You could use a WeakSet to keep track of the objects that were already traversed:

 function traverseOnce(obj, cb) {
   const visited = new WeakSet();
   (function traverse(obj) {
     for(const [key, value] of Object.entries(obj)) {
       if(typeof value === "object" && value !== null) {
          if(visited.has(value)) continue;
          visited.add(value);
          cb(value);
          traverse(value);
       }
      }
   })(obj);
 }

Through the WeakSet you got O(1) lookup time, and are also sure that this will never leak.

Usable as:

 const nested = { other: { a: 1 } };
 nested.self = nested;

 traverseOnce(nested, console.log);
 // nested: { other, self }
 // other: { a: 1 }

You could also use a Symbol to flag traversed objects, for that replace new WeakSet() with Symbol(), visited.has(value) with value[visited] and visuted.add(value) with value[visited] = true;

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement