3 回答

TA貢獻1805條經(jīng)驗 獲得超10個贊
在開始之前,我們必須首先解決輸入數(shù)據(jù)的不規(guī)則形狀 -
const data2 =
? { name:"root"
? , children:
? ? ? Array.from
? ? ? ? ( Object.entries(data.root)
? ? ? ? , ([ country, _ ]) =>
? ? ? ? ? ? Object.assign({ name:country }, _)
? ? ? ? )
? }
console.log(JSON.stringify(data2, null, 2))
現(xiàn)在我們看到的data2是一個統(tǒng)一的{ 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" }
? ? ? ]
? ? }
? ]
}
現(xiàn)在我們編寫一個通用的深度優(yōu)先遍歷函數(shù),dft-
function* dft (t, path = [])
{ for (const _ of t.children ?? [])
? ? yield* dft(_, [...path, t.name ])
? yield [path, t]
}
我們的dft函數(shù)為我們的輸入樹中的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"}
現(xiàn)在我們知道了每個節(jié)點的路徑,我們可以創(chuàng)建一個index使用path和 anysearch_words鏈接回該節(jié)點的路徑 -
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,我們有一種方法可以為任何搜索詞創(chuàng)建快速查找 -
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"}]? ?
}
這應該突出顯示數(shù)據(jù)中剩余的不一致之處。例如,您有一些嵌套在city、capital或下的節(jié)點borough。另外值得注意的是,我們可能應該使用s.toLowerCase()所有索引鍵,以便查找不區(qū)分大小寫。這是留給讀者的練習。
創(chuàng)建index很容易,您只需要做一次-
const myIndex =?
? index(data2)
您的索引可以根據(jù)需要重復用于任意多次查找 -
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 應用程序由您完成。

TA貢獻1851條經(jīng)驗 獲得超5個贊
不完全清楚您要從問題中尋找什么,但我猜您需要修改數(shù)據(jù)以確保在渲染時正確突出顯示匹配的數(shù)據(jù)?
這是使用對象掃描查找匹配對象的解決方案
// const objectScan = require('object-scan');
const myData = { 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' }] } } };
// eslint-disable-next-line camelcase
const mySearchFn = (term) => ({ name, search_words = [] }) => name === term || search_words.includes(term);
const search = (input, searchFn) => objectScan(['**[*]'], {
? filterFn: ({ value, context }) => {
? ? if (searchFn(value)) {
? ? ? const { children, ...match } = value;
? ? ? context.push(match);
? ? }
? }
})(input, []);
console.log(search(myData, mySearchFn('Toronto')));
// => [ { name: 'Toronto' } ]
console.log(search(myData, mySearchFn('borough')));
// => [ { name: 'Westminster', search_words: [ 'borough' ] } ]
console.log(search(myData, mySearchFn('capital')));
// => [ { name: 'Paris', search_words: [ 'city', 'capital' ] }, { name: 'London', search_words: [ 'city', 'capital' ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>
這就是您如何注入信息,然后渲染管道可以獲取這些信息
// const objectScan = require('object-scan');
const myData = { 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' }] } } };
// eslint-disable-next-line camelcase
const mySearchFn = (term) => ({ name, search_words = [] }) => name === term || search_words.includes(term);
const search = (input, searchFn) => objectScan(['**[*]'], {
? filterFn: ({ value }) => {
? ? if (searchFn(value)) {
? ? ? value.css = { highlight: true };
? ? ? return true;
? ? } else {
? ? ? delete value.css;
? ? ? return false;
? ? }
? },
? rtn: 'count' // return number of matches
})(input);
console.log(search(myData, mySearchFn('Toronto')));
// => 1
console.log(myData);
// => { 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', css: { highlight: true } }, { name: 'Ottawa' } ] }, { name: 'United States' } ] } } }
console.log(search(myData, mySearchFn('borough')));
// => 1
console.log(myData);
// => { root: { Europe: { children: [ { name: 'Germany' }, { name: 'England', children: [ { name: 'London', search_words: [ 'city', 'capital' ], children: [ { name: 'Westminster', search_words: [ 'borough' ], css: { highlight: true } } ] }, { 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' } ] } } }
console.log(search(myData, mySearchFn('capital')));
// => 2
console.log(myData);
// => { root: { Europe: { children: [ { name: 'Germany' }, { name: 'England', children: [ { name: 'London', search_words: [ 'city', 'capital' ], children: [ { name: 'Westminster', search_words: [ 'borough' ] } ], css: { highlight: true } }, { name: 'Manchester', search_words: [ 'city' ] } ] }, { name: 'France', children: [ { name: 'Paris', search_words: [ 'city', 'capital' ], css: { highlight: true } } ] } ] }, 'North America': { children: [ { name: 'Canada', children: [ { name: 'Toronto' }, { name: 'Ottawa' } ] }, { name: 'United States' } ] } } }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>
使用庫對你來說可能不值得,這是一種權衡。如果您對此答案有疑問/想法,請告訴我。

TA貢獻1864條經(jīng)驗 獲得超6個贊
正如謝謝指出的那樣,不一致的數(shù)據(jù)格式使得為此編寫好的代碼變得更加困難。我的方法略有不同。我沒有轉換數(shù)據(jù),而是為通用函數(shù)編寫了一個包裝器,以便以更有用的方式處理此輸出。
我們從一個函數(shù)開始collect,它將遞歸地處理{name?, search_words?, children?, ...rest}對象,返回與給定謂詞匹配的節(jié)點并在子節(jié)點上重復出現(xiàn)。我們用函數(shù) 來調用它,search它接受一個搜索詞并從中創(chuàng)建一個謂詞。(這里我們測試是否name或任何search_term匹配該術語;對于部分匹配、不區(qū)分大小寫等,這很容易修改。)
然后我們編寫我提到的包裝器searchLocations。它下降到節(jié)點,然后映射并組合調用每個根值.root的結果。search
const collect = (pred) => ({children = [], ...rest}) => [
... (pred (rest) ? [rest] : []),
... children .flatMap (collect (pred))
]
const search = (term) =>
collect (({name = '', search_words = []}) => name == term || search_words .includes (term))
const searchLocations = (locations, term) =>
Object.values (locations .root) .flatMap (search (term))
const locations = {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"}]}}}
console .log ('Toronto', searchLocations (locations, 'Toronto'))
console .log ('borough', searchLocations (locations, 'borough'))
console .log ('capital', searchLocations (locations, 'capital'))
.as-console-wrapper {max-height: 100% !important; top: 0}
如果您想要的(聽起來像是您可能想要的)與輸入的結構相同,僅保留包含匹配項所需的節(jié)點,那么我們應該能夠從樹過濾函數(shù)開始執(zhí)行類似的操作。假期后我會嘗試看看。
更新
我確實又看了一遍,希望將樹作為一棵樹來過濾。代碼并沒有那么難。但這一次,我確實使用了一個convert函數(shù)將您的數(shù)據(jù)轉換為更一致的遞歸結構。因此,整個對象變成一個數(shù)組,根部有兩個元素,一個元素為name“歐洲”,另一個元素為name“北美”,每個元素都有其現(xiàn)有children節(jié)點。這使得所有進一步的處理變得更加容易。
這里有兩個關鍵函數(shù):
第一個是通用deepFilter函數(shù),它采用謂詞和項目數(shù)組,這些項目的節(jié)點children結構可能類似于其父級,并返回一個新版本,其中包含與謂詞及其完整祖先匹配的任何內容。它看起來像這樣:
const deepFilter = (pred) => (xs) =>
xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilter (pred) (children)) =>
pred (rest) || kids.length
? [{...rest, ...(kids.length ? {children: kids} : {})}]
: []
)
第二個是專門針對這個問題的:searchLocation。它調用deepFilter使用由搜索項和已討論的轉換結構構造的謂詞。它使用convert結構的幫助器,以及search將搜索詞轉換為謂詞的幫助器,該謂詞在名稱和所有搜索詞上查找(不區(qū)分大小寫)部分匹配。
const searchLocations = (loc, locations = convert(loc)) => (term) =>
term.length ? deepFilter (search (term)) (locations) : locations
這通過用戶界面來演示,該用戶界面顯示嵌套中的位置<UL>,并帶有實時過濾位置的搜索框。
例如,如果您在搜索框中輸入“w”,您將得到
Europe
England
London (city, capital)
Westminster (borough)
North America
Canada
Ottawa
因為“Westminster”和“Ottawa”是唯一的匹配項。
如果你輸入“城市”你會得到
Europe
England
London (city, capital)
Manchester (city)
France
Paris (city, capital)
您可以在此代碼片段中看到它的實際效果:
// utility function
const deepFilter = (pred) => (xs) =>
xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilter (pred) (children)) =>
pred (rest) || kids.length
? [{...rest, ...(kids.length ? {children: kids} : {})}]
: []
)
// helper functions
const search = (t = '', term = t.toLowerCase()) => ({name = '', search_words = []}) =>
term.length && (
name .toLowerCase () .includes (term) ||
search_words .some (word => word .toLowerCase() .includes (term))
)
const convert = ({root}) =>
Object.entries (root) .map (([name, v]) => ({name, ...v}))
// main function
const searchLocations = (loc, locations = convert(loc)) => (term) =>
term.length ? deepFilter (search (term)) (locations) : locations
// sample data
const myData = { 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' }] } } };
// main function specialized to given data
const mySearch = searchLocations(myData)
// UI demo
const format = (locations, searchTerm) => `<ul>${
locations.map(loc => `<li>${
loc.name +
(loc.search_words ? ` (${loc.search_words. join(', ')})` : ``) +
(loc.children ? format(loc.children, searchTerm) : '')
}</li>`)
.join('')
}</ul>`
const render = (locations, searchTerm) =>
document .getElementById ('main') .innerHTML = format (locations, searchTerm)
document .getElementById ('search') .addEventListener (
'keyup',
(e) => render (mySearch (e.target.value))
)
// show demo
render (mySearch (''))
<div style="float: right" id="control">
<label>Search: <input type="text" id="search"/></label>
</div>
<div style="margin-top: -1em" id="main"></div>
顯然,這并沒有使用Vue來生成樹,只是進行一些字符串操作和innerHTML. 我把這部分留給你。但它應該顯示另一種過濾嵌套結構的方法。
添加回答
舉報