0

How do I loop through nested objects in a clean manner? I am trying to get the value at the third level of an object of this structure:

const myObj = {
  firstObj: {
    nested: {
      anotherNest: 'i want to get this value'
    },
    again: {
      'i want this value ->': ':D'
    }
  }
}

The obvious solution would be three nested for loops:

for (const key1 of Object.keys(myObj))
  for (const key2 of Object.keys(myObj[key1]))
    for (const key3 of Object.keys(myObj[key1][key2]))
      ...

This solution is not clean at all (and not scale able). How can I solve this?

timgfx
  • 199
  • 2
  • 14
  • Does this answer your question? [Iterate through Nested JavaScript Objects](https://stackoverflow.com/questions/8085004/iterate-through-nested-javascript-objects) – critrange Jul 28 '20 at 18:44

1 Answers1

3

What you're pretty much doing here is iterating through values at the third level of an object. You could create an iterator to do this for you. Generator functions are ideal for this, as you can easily use them recursively. The following code (in Typescript) will allow you to easily do this:

type NestedObjects <T> = {
  [key: string]: NestedObjects <T> | T
}

/**
 * Iterates through all values nested at n levels deep.
 * Every iteration yields an array containing all keys looped through, in reverse.
 * This means that the first entry will be the value at n levels, followed by keys at n-1, n-2, etc.
 *
 * Example:
 * <pre>
 * for (const x of NestedObjectIterator(a, 3)) ...
 * </pre>
 *
 * is equivalent to three nested for loops:
 * <pre>
 * for (const x of Object.keys(a))
 *  for (const y of Object.keys(a[x]))
 *   for (const z of Object.keys(a[x][y])) ...
 * </pre>
 */
function * NestedObjectIterator <T> (obj: NestedObjects<T>, levels: number, ...prevKeys: string[]): Iterable<[T, ...string[]]> {
  for (const key of Object.keys(obj)) {
    if (levels === 1) yield [obj[key] as T, key, ...prevKeys]
    else if (obj[key]) yield * NestedObjectIterator(obj[key] as NestedObjects<T>, levels - 1, key, ...prevKeys)
  }
}

The equivalent in pure Javascript:

function * NestedObjectIterator (obj, levels, ...prevKeys) {
  for (const key of Object.keys(obj)) {
    if (levels === 1) yield [obj[key], key, ...prevKeys]
    else if (obj[key]) yield * NestedObjectIterator(obj[key], levels - 1, key, ...prevKeys)
  }
}

You can now loop through your object like this:

const myObj = {
  firstObj: {
    nested: {
      anotherNest: 'i want to get this value'
    },
    again: {
      'i want this value ->': ':D'
    }
  }
}

for (const [value] of NestedObjectIterator(myObj, 3)) {
  console.log(value)
}
timgfx
  • 199
  • 2
  • 14