首页 > 解决方案 > 如何使用 vue/javascript 通过多个属性过滤深度嵌套的 json

问题描述

我有一些具有以下结构的 JSON:

{
  "root": {
    "Europe": {
      "children": [
        {
          "name": "Germany"
        },
        {
          "name": "England",
          "children": [
            {
              "name": "London",
              "search_words": ["city", "capital"],
              "children": [
                {
                  "name": "Westminster",
                  "search_words": ["borough"]
                }
              ]
            },
            {
              "name": "Manchester",
              "search_words": ["city"]
            }
          ]
        },
        {
          "name": "France",
          "children": [
            {
              "name": "Paris",
              "search_words": ["city", "capital"]
            }
          ]
        }
      ]
    },
    "North America": {
      "children": [
        {
          "name": "Canada",
          "children": [
            {
              "name": "Toronto"
            },
            {
              "name": "Ottawa"
            }
          ]
        },
        {
          "name": "United States"
        }
      ]
    }
  }
}

我想根据文本搜索过滤 JSON。我应该能够同时搜索 thename和 any search_words。最后一个问题是 JSON 可以任意深,因此它需要能够搜索所有级别。

我还需要循环并打印出 HTML 中的 JSON(使用 Vue),并根据搜索进行更新。目前尚不清楚如何在不知道 JSON 将进入多少级别的情况下做到这一点?

任何帮助将不胜感激!

标签: javascriptjsonvue.jsrecursion

解决方案


我最近回答了一个类似的问题。我在这里分享它是因为我认为它为这篇文章提供了相关的基础。不过,在开始之前,我们必须首先解决输入数据的不规则形状 -

const data2 =
  { name:"root"
  , children:
      Array.from
        ( Object.entries(data.root)
        , ([ country, _ ]) =>
            Object.assign({ name:country }, _)
        )
  }

console.log(JSON.stringify(data2, null, 2))

现在我们可以看到data2是一个统一的{ name, children: [ ... ]}形状——

{
  "name": "root",
  "children": [
    {
      "name": "Europe",
      "children": [
        { "name": "Germany" },
        {
          "name": "England",
          "children": [
            {
              "name": "London",
              "search_words": [ "city", "capital" ],
              "children": [
                {
                  "name": "Westminster",
                  "search_words": [ "borough" ]
                }
              ]
            },
            {
              "name": "Manchester",
              "search_words": [ "city" ]
            }
          ]
        },
        {
          "name": "France",
          "children": [
            {
              "name": "Paris",
              "search_words": [ "city", "capital" ]
            }
          ]
        }
      ]
    },
    {
      "name": "North America",
      "children": [
        {
          "name": "Canada",
          "children": [
            { "name": "Toronto" },
            { "name": "Ottawa" }
          ]
        },
        { "name": "United States" }
      ]
    }
  ]
}

现在我们编写一个通用的深度优先遍历函数,dft-

function* dft (t, path = [])
{ for (const _ of t.children ?? [])
    yield* dft(_, [...path, t.name ])
  yield [path, t]
}

我们的dft函数为我们的输入树中的path每个元素, -et

["root","Europe"]
{"name":"Germany"}

["root","Europe","England","London"]
{name:"Westminster", search_words:["borough"]}

["root","Europe","England"]
{name:"London", search_words:["city","capital"], children:[...]}

["root","Europe","England"]
{name:"Manchester", search_words:["city"]}

["root","Europe"]
{name:"England", children:[...]}

["root","Europe","France"]
{name:"Paris", search_words:["city","capital"]}

["root","Europe"]
{name:"France", children:[...]}

["root"]
{name:"Europe", children:[...]}

["root","North America","Canada"]
{name:"Toronto"}

现在我们知道每个节点的路径,我们可以创建一个index使用 thepath和 anysearch_words链接回节点的路径 -

const index = t =>
  Array.from
    ( dft(t)
    , ([path, e]) =>
        [ [...path, e.name, ...e.search_words ?? [] ] // all words to link to e
        , e                                           // e
        ]
    )
    .reduce
      ( (m, [ words, e ]) =>
          insertAll(m, words, e) // update the index using generic helper
      , new Map
      )

这取决于通用助手insertAll-

const insertAll = (m, keys, value) =>
  keys.reduce
    ( (m, k) =>
        m.set(k, [ ...m.get(k) ?? [], value ])
    , m
    )

完成后,index我们可以为任何搜索词创建快速查找 -

const myIndex = 
  index(data2)

console.log(myIndex)
Map 
{ "Europe" =>
    [{"name":"Germany"},{"name":"Westminster",...},{"name":"London",...},{"name":"Manchester",...},{"name":"England"...},{"name":"Manchester",...}]},{"name":"Paris",...},{"name":"France"...},{"name":"Europe"...},{"name":"Manchester",...}]},{"name":"France"...}]}]

, "Germany" => 
    [{"name":"Germany"}]

, "England" =>
    [{"name":"Westminster",...},{"name":"London",...},{"name":"Manchester",...},{"name":"England"...},{"name":"Manchester",...}]}]

, "London" =>
    [{"name":"Westminster",...},{"name":"London",...}]

, "Westminster" =>
    [{"name":"Westminster",...}]

, "borough" =>
    [{"name":"Westminster",...}]

, "city" =>
    [{"name":"London",...},{"name":"Manchester",...},{"name":"Paris",...}]

, "capital" =>
    [{"name":"London",...},{"name":"Paris",...}]

, "Manchester" =>
    [{"name":"Manchester",...}]

, "France" =>
    [{"name":"Paris",...},{"name":"France"...}]

, "Paris" =>
    [{"name":"Paris",...}]

, "North America" =>
    [{"name":"Toronto"},{"name":"Ottawa"},{"name":"Canada"...},{"name":"United States"},{"name":"North America"...},
    {"name":"United States"}]}]

, "Canada" =>
    [{"name":"Toronto"},{"name":"Ottawa"},{"name":"Canada"...}]

, "Toronto" =>
    [{"name":"Toronto"}]

, "Ottawa" =>
    [{"name":"Ottawa"}]

, "United States" =>
    [{"name":"United States"}]   
}

这应该突出显示数据中剩余的不一致之处。例如,您有一些节点嵌套在citycapital或下borough。还值得注意的是,我们可能应该s.toLowerCase()在所有索引键上使用,以便查找可以不区分大小写。这是留给读者的练习。

创建index很容易,您只需要做一次-

const myIndex = 
  index(data2)

您的索引可以根据需要重复用于尽可能多的查找 -

console.log(myIndex.get("Toronto") ?? [])
console.log(myIndex.get("France") ?? [])
console.log(myIndex.get("Paris") ?? [])
console.log(myIndex.get("Canada") ?? [])
console.log(myIndex.get("Zorp") ?? [])
[{"name":"Toronto"}]
[{"name":"Paris",...},{"name":"France"...}]
[{"name":"Paris",...}]
[{"name":"Toronto"},{"name":"Ottawa"},{"name":"Canada"...}]
[]

在你的 Vue 应用程序中插入结果是留给你的。


推荐阅读