Как получить значение свойства JSON Схема дерева, используя JavaScript? - PullRequest
0 голосов
/ 01 февраля 2020

У меня есть JSON древовидная структура, подобная этой.

[
  {
    "title": "Blogs",
    "id": "blogs",
    "type": "array",
    "children": [
      {
        "title": "Today",
        "id": "today",
        "type": "string"
      },
      {
        "title": "Yesterday",
        "id": "yesterday",
        "type": "enum",
        "options": [
          "Y1",
          "Y2"
        ]
      }
    ]
  },
  {
    "title": "Links",
    "id": "links",
    "type": "object",
    "children": [
      {
        "title": "Oracle",
        "id": "oracle",
        "children": [
          {
            "title": "USA",
            "id": "usa",
            "type": "array",
            "children": [
              {
                "title": "Midwest",
                "id": "midwest",
                "type": "enum",
                "options": [
                  "Md1",
                  "Md2"
                ]
              },
              {
                "title": "West",
                "id": "west",
                "type": "boolean"
              }
            ]
          },
          {
            "title": "Asia",
            "id": "asia",
            "type": "array",
            "children": [
              {
                "title": "India",
                "id": "india",
                "type": "string"
              }
            ]
          }
        ]
      }
    ]
  }
]

Мне нужна рекурсивная функция, которая принимает 2 аргумента (1-й аргумент - это фактические данные дерева, а 2-й аргумент - это путь с точечной нотацией) и возвращает тип узла (string / object / array / boolean) и значения enum, если типом является enum. Точечный путь обозначения может содержать индекс массива как 0 или 1 или около того. В основном то, что я хочу, это

var nodeType = getType(treeData, 'links.oracle.usa.0.midwest');  // Note: there is a 0 as usa is an array type
console.log(nodeType); // Should return [{"type":"enum"},{"options": ["md1", "md2"]}]

var nodeType = getType(treeData, 'blogs.0.today');
console.log(nodeType); // Should return [{"type":"string"}]

Ответы [ 3 ]

2 голосов
/ 01 февраля 2020

Похоже на рабочий код, который также обрабатывает неправильные пути:

const sample = [
  {
    "title": "Blogs",
    "id": "blogs",
    "type": "array",
    "children": [
      {
        "title": "Today",
        "id": "today",
        "type": "string"
      },
      {
        "title": "Yesterday",
        "id": "yesterday",
        "type": "enum",
        "options": [
          "Y1",
          "Y2"
        ]
      }
    ]
  },
  {
    "title": "Links",
    "id": "links",
    "type": "object",
    "children": [
      {
        "title": "Oracle",
        "id": "oracle",
        "children": [
          {
            "title": "USA",
            "id": "usa",
            "type": "array",
            "children": [
              {
                "title": "Midwest",
                "id": "midwest",
                "type": "enum",
                "options": [
                  "Md1",
                  "Md2"
                ]
              },
              {
                "title": "West",
                "id": "west",
                "type": "boolean"
              }
            ]
          },
          {
            "title": "Asia",
            "id": "asia",
            "type": "array",
            "children": [
              {
                "title": "India",
                "id": "india",
                "type": "string"
              }
            ]
          }
        ]
      }
    ]
  }
]

const getType = (tree, path) => {
  if (!path.length) return

  const element = getElementFromTree(tree, path.split('.'))
  if (!element || !element.type) return

  const res = [{ type: element.type }]
  if (element.options) {
    res.push({ options: element.options })
  }
  return res
}

const getElementFromTree = (treePart, path) => {
  const prop = path.shift()
  if (!path.length) {
    return treePart.id === prop ? treePart : undefined
  }

  let nextTreePart;
  if (Array.isArray(treePart)) {
    nextTreePart = treePart.find(v => v.id === prop)
  } else if (isNaN(prop)) {
    nextTreePart = treePart.children.find(v => v.id === prop)
  } else {
    nextTreePart = treePart.children[prop]
  }

  if (!nextTreePart) return
  if (path.length) {
    return getElementFromTree(nextTreePart, path)
  }
  return nextTreePart
}

// work as expected:
console.log(getType(sample, 'links.oracle.usa.0.midwest'))
console.log(getType(sample, 'links.oracle.usa.1.west'))
console.log(getType(sample, 'blogs.0.today'))
console.log(getType(sample, 'blogs.1.yesterday'))
console.log(getType(sample, 'links.oracle.asia.0.india'))

// tests with wrong paths, all return undefined
console.log(getType(sample, 'links.oracle.usa.5.west')) // because 5th element doesn't exists
console.log(getType(sample, 'blogs.3.today')) // because 3rd element doesn't exists
console.log(getType(sample, 'links.oracle')) // because links.oracle doesn't contain type field in it
console.log(getType(sample, '10.this.is.wrong.path')) // because path doesn't exist at all

Надеюсь, это поможет <3 </p>

1 голос
/ 04 февраля 2020

Я бы предпочел разбить это на несколько функций. Код поиска по дереву начинается с пути типа ["links", "oracle", "usa", "midwest"] и объекта данных со свойством массива children, возвращающего узел по этому пути, или undefined, если он не существует.

Тогда мы напишем простую оболочку для преобразования вашей строки "links.oracle.usa.0.midwest" в этот массив и преобразования вашего входного массива в свойство children нового объекта. Эта getNode функция также возвращает узел или undefined. Это независимо полезная функция.

Тогда, поскольку вы в конечном итоге захотите тип узла, мы добавим простую оболочку getType для отчета о типе узла или "unknown", если он не найден. Мы можем легко заменить "unknown" на undefined или что угодно, что вы выберете очевидным образом.

const findInTree = (xs) => ([p = undefined, ...ps]) =>
  xs == undefined 
    ? undefined
  : p == undefined
    ? xs
  : findInTree (xs .children .find (({id}) => id == p)) (ps)

const getNode = (xs) => (path) =>
  findInTree ({children: xs}) (path .split ('.') .filter (isNaN))

const getType = (xs) => (path) =>
  (getNode (xs) (path) || {type: 'unknown'}) .type

const data = [{title: "Blogs", id: "blogs", type: "array", children: [{title: "Today", id: "today", type: "string"}, {title: "Yesterday", id: "yesterday", type: "enum", options: ["Y1", "Y2"]}]}, {title: "Links", id: "links", type: "object", children: [{title: "Oracle", id: "oracle", children: [{title: "USA", id: "usa", type: "array", children: [{title: "Midwest", id: "midwest", type: "enum", options: ["Md1", "Md2"]}, {title: "West", id: "west", type: "boolean"}]}, {title: "Asia", id: "asia", type: "array", children: [{title: "India", id: "india", type: "string"}]}]}]}];

console .log (getType (data) ("links.oracle.usa.0.midwest")) //~> "enum"
console .log (getType (data) ("links.oracle.usa"))           //~> "array"
console .log (getType (data) ("blogs.0.today"))              //~> "string"
console .log (getType (data) ("blogs.2.tomorrow"))           //~> "unknown"

Все эти функции довольно просты. Рекурсия ясна; Распределение обязанностей должно быть простым.

Но я должен был сделать предположение здесь. Как указано в другом ответе, индекс массива и следующий идентификатор являются избыточными. Мы могли бы добавить сложности к рекурсивной функции, чтобы справиться с этим случаем, но это привело бы к уродливому коду. Вместо этого перед обработкой узла мы удаляем индекс массива. Вот для чего .filter (isNaN) используется в getNode. Если это нежелательное поведение, например, если вы захотите потерпеть неудачу или вернете undefined, если индекс и идентификатор не совпадают, то нам придется сделать что-то совсем другое. Я действительно не следовал вашему обоснованию необходимости индекса и идентификатора, но в комментарии к другому ответу вы, похоже, подразумеваете, что это идентификатор, который вам действительно нужен. Если это и то и другое, то эта техника потребует тяжелой и ужасной модификации.

0 голосов
/ 01 февраля 2020

Использование Loda sh get метод позволяет: _.get(object, 'a[0].b.c');. Это безопасно в отношении получения значения из несуществующего пути - не будет выдавать ошибку.

...