sonnyp / JSON8

JSON toolkit for JavaScript.
ISC License
104 stars 13 forks source link

Feat request: Expose walk/iterator interface for JSON Pointer #110

Open beorn opened 4 years ago

beorn commented 4 years ago

Hi, currently the pointer package only exposes post-iteration dicts/index - it would be nice if the walk function an iterator/generator interface was exposed, e.g.,

for (const [ptr, val] of pointer.entries(myPointer))
  ...

Maybe entries, flatEntries, primitives, or something like that, to select what to iterate over.

beorn commented 4 years ago

I cobbled together one:

import { join } from "json8-pointer"

/* JSON Pointer iterator
 *
 * yields [ entryPath, entryObj, entryKey, parentPath, parentObj ]
 * but values are of course ignorable, so this works fine:
 *
 * for (const [key, val] of entries(obj)) console.log(key, val)
 */
export function* entries(tree) {
  const isContainer = (o) => typeof o === "object" && o !== null
  const isEnumerable = (o) => Array.isArray(o) || o instanceof Set
  const isKeyvalue = (o) => typeof o === "object" || o instanceof Map

  yield [[], tree]
  yield* children([], tree)

  function* children(parentPath, parentObj) {
    if (!isContainer(parentObj)) return
    if (isEnumerable(parentObj)) {
      let c = 0
      for (const childObj of parentObj) {
        const childKey = String(c++)
        const childPath = join(parentPath, childKey)
        yield [childPath, childObj, childKey, parentPath, parentObj]
        yield* children(childPath, childObj)
      }
      return
    }
    if (isKeyvalue(parentObj)) {
      for (const [childKey, childObj] of Object.entries(parentObj)) {
        const childPath = join(parentPath, childKey)
        yield [childPath, childObj, childKey, parentPath, parentObj]
        yield* children(childPath, childObj)
      }
      return
    }
    throw new TypeError(parentObj + " is not a structure")
  }
}