如何干净地遍历嵌套对象?

时间:2020-07-28 18:18:03

标签: javascript typescript

如何以一种干净的方式遍历嵌套对象?我正在尝试在此结构的对象的第三级获取值:

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

显而易见的解决方案是三个嵌套的for循环:

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

该解决方案根本不是干净的(并且不能缩放)。我该如何解决?

1 个答案:

答案 0 :(得分:3)

您在这里要做的几乎是遍历对象第三级的值。您可以创建一个迭代器来为您执行此操作。生成器函数非常适合此操作,因为您可以轻松地递归使用它们。以下代码(在Typescript中)将使您轻松地做到这一点:

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)
  }
}

相当于纯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)
  }
}

您现在可以像这样遍历对象:

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)
}