如何使用 vue/javascript 通过多个属性过滤深度嵌套的 json [英] How to filter deeply nested json by multiple attributes with vue/javascript

查看:33
本文介绍了如何使用 vue/javascript 通过多个属性过滤深度嵌套的 json的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have some JSON with the following structure:

{
  "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.我应该能够通过 name 和任何 search_words 进行搜索.最后一个问题是 JSON 可以任意深度,因此需要能够搜索所有级别.

I want to filter the JSON based on a text search. I should be able to search by both the name and any search_words. The final problem is that the JSON can be arbitrarily deep so it needs to be able to search through all levels.

我还需要遍历并打印出 HTML 格式的 JSON(使用 Vue),并根据搜索结果进行更新.目前不清楚如何在不知道 JSON 会深入多少层的情况下做到这一点?

I also need to loop through and print out the JSON in HTML (using Vue) and for it to update based on the search. Currently unclear how I can do this without knowing how many levels deep the JSON will go?

任何帮助将不胜感激!

推荐答案

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

I answered a similar question recently. I'm sharing it here because I think it provides a relevant foundation for this post. Before we begin though, we must first address the irregular shape of your input data -

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: [ ... ]} 形状 -

Now we can see data2 is a uniform { name, children: [ ... ]} shape -

{
  "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 -

Now we write a generic depth-first traversal function, dft -

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

我们的 dft 函数为我们提供了一个 path 到我们输入树中的每个元素 et-

Our dft function gives us a path to each element, e, in our input tree, t -

["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,它使用 path 和任何 search_words 来链接回到节点-

Now that we know that path to each of the nodes, we can create an index which uses the path and any search_words to link back to the node -

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 -

This depends on a generic helper insertAll -

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

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

With index finished, we have a way to create a fast lookup for any search term -

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"}]   
}

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

This should highlight the remaining inconsistencies in your data. For example, you have some nodes nested under city, capital, or borough. Also worth noting that we should probably use s.toLowerCase() on all of the index keys so that lookups can be case-insensitive. This is an exercise left for the reader.

创建index 很简单,你只需要一次 -

Creating the index is easy and you only need to do it once -

const myIndex = 
  index(data2)

您的索引可以根据需要重复使用进行多次查找 -

Your index can be reused for as many lookups as you need -

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 应用程序中插入结果由您决定.

Inserting the results in you Vue application is left for you.

这篇关于如何使用 vue/javascript 通过多个属性过滤深度嵌套的 json的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆