0

我正在尝试获取在 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'])
  })
})

如果有人对如何收集已经访问过的模式以使递归函数停止有想法,他们将不胜感激。

4

1 回答 1

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
}
于 2017-08-24T12:27:31.667 回答