2

How would one iterate trough a deeply nested object while retrieving the path of each key that contains a value.

Demo input Object

{
  nested: { 
    nested2: { 
      nested3: '123' 
    },
    nested4: '456' 
  }
}

wanted output

nested.nested2.nested3
nested.nested4

3 Answers 3

3

You can recursively visit nodes by keeping a stack reference. I modified the object by adding an example of an array.

I simply join the path with dot separators and then transform numeric keys into bracket notation using the following regular expression.

/(?:\.)(\d+)(?![a-z_])/ig

I combined a non-matching group (starts with a dot) and with a negative lookahead (does not precede a letter).

Alternatively, you can use the following expression:

/(?:\.)(\d+)(?=\.)/g

I replaced the negative lookahead with a positive lookahead. I essentially inverted the expression, which may be better for this situation.

const obj = {
  nested: { 
    nested2: [
      { nested3: '123' },
      { nested4: '456' }
    ],
    nested5: '789' 
  }
};

const visitNodes = (obj, visitor, stack = []) => {
  if (typeof obj === 'object') {
    for (let key in obj) {
      visitNodes(obj[key], visitor, [...stack, key]);
    }
  } else {
    visitor(stack.join('.').replace(/(?:\.)(\d+)(?![a-z_])/ig, '[$1]'), obj);
  }
}

visitNodes(obj, (path, value) => console.log(`${path} = ${value}`));

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much, I couldn't keep track of the stack in what i wrote.
@GregManiak I fixed the regex flags and updated/fixed some wording.
1

Here's a concise solution using Array.reduce:

const obj = {
  nested: {
    nested2: {
      nested3: '123'
    },
    nested4: '456'
  }
};

const r = (e, c = "") => Object.keys(e).reduce((t, y) => Array.isArray(e[y]) ? t : "object" == typeof e[y] ? [...t, ...r(e[y], c + y + ".")] : [...t, c + y], []);

var s = r(obj)
console.log(s);

2 Comments

Lovely syntax - and so intuitive too ;)
I would approach it this way too, but it could use longer names for some of the variables and some line breaks.
0

You will have to recurse through the object's paths until it reaches something that isn't an object, at which point it should log the path up to that point.

var x = {
  nested: {
    nested2: {
      nested3: '123'
    },
    nested4: '456'
  }
};


const printPaths = (obj, parentPath) => {
  if (typeof obj === "object" && !Array.isArray(obj)) {
    const layerKeys = Object.keys(obj);
    for (const ky of layerKeys) {
      const newKey = parentPath ? `${parentPath}.${ky}` : ky;
      printPaths(obj[ky], newKey);
    }
  } else if (parentPath) {
    console.log(parentPath);
  }
};

printPaths(x);

Comments

Your Answer

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