获取在嵌套模式

时间:2017-08-23 11:46:10

标签: javascript normalizr

我试图获取在normalizr架构上定义的所有键的列表,&编写了一个功能,可以完成我对简单模式的需求:

export const collectAttributes = target => {

  const schemaKeys = []

  if (target.hasOwnProperty('_key')) {
    schemaKeys.push(target._key)
  }

  const definitions = Object.keys(target).filter(key => key[0] !== '_')

  definitions.forEach(key => {
    collectAttributes(target[key]).forEach(attribute => schemaKeys.push(attribute))
  })

  return schemaKeys
}

但是,如果出现Maximum call stack size exceeded错误,则嵌套模式定义失败,如此测试用例所示:

describe('collectAttributes', () => {
  it('should collect all unique collections defined on a recursive schema', () => {
    const nodeSchema = new schema.Entity('nodes', {})
    const nodeListSchema = new schema.Array(nodeSchema)
    nodeSchema.define({ children: nodeListSchema })

    expect(collectAttributes(nodeSchema)).toEqual(['nodes'])
  })
})

如果有人有关于如何收集已经访问过的模式以使递归函数停止的想法,那么他们将非常感激。

1 个答案:

答案 0 :(得分:0)

我最终想出来了 - 解决方案如下:

export const isSchema = target => {
  if (Array.isArray(target)) {
    return target.length ? isSchema(target[0]) : false
  } else {
    return target.hasOwnProperty('schema') || target instanceof schema.Entity || target instanceof schema.Array
  }
}

const recursiveCollect = (target, visited = []) => {

  const entities = []
  const visitedSchemas = [...visited]

  if (isSchema(target)) {
    entities.push(target.key)
    visitedSchemas.push(target)
  }

  if (Array.isArray(target) || target instanceof schema.Array) {
    /* 
     * If the current target is an ArraySchema, call `recursiveCollect` 
     * on the underlying entity schema 
     */
    return recursiveCollect(target.schema, visitedSchemas)
  }

  Object.keys(target.schema).filter(x => x[0] !== '_').forEach(definition => {

    const childSchema= target.schema[definition]
    const alreadyVisited = visitedSchemas.includes(childSchema)

    if (isSchema(childSchema) && !alreadyVisited) {
      /* Only call `recursiveCollect` on the child schema if it hasn't 
       * already been encountered
       */
      const result = recursiveCollect(childSchema, visitedSchemas)

      if (result.entities) {
        result.entities.forEach(x => entities.push(x))
      }
      if (result.visitedSchemas) {
        result.visitedSchemas.forEach(x => visitedSchemas.push(x))
      }
    }
  })

  return { entities, visitedSchemas }
}


export const collectAttributes = target => {
  const { entities } = recursiveCollect(target)
  return entities
}