-
holdtom
在开始之前,我们必须首先解决输入数据的不规则形状 -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使用path和 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"}] }这应该突出显示数据中剩余的不一致之处。例如,您有一些嵌套在city、capital或下的节点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 应用程序由您完成。
-
江户川乱折腾
不完全清楚您要从问题中寻找什么,但我猜您需要修改数据以确保在渲染时正确突出显示匹配的数据?这是使用对象扫描查找匹配对象的解决方案// 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 camelcaseconst 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 camelcaseconst 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')));// => 1console.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')));// => 1console.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')));// => 2console.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>使用库对你来说可能不值得,这是一种权衡。如果您对此答案有疑问/想法,请告诉我。
-
慕尼黑的夜晚无繁华
正如谢谢指出的那样,不一致的数据格式使得为此编写好的代码变得更加困难。我的方法略有不同。我没有转换数据,而是为通用函数编写了一个包装器,以便以更有用的方式处理此输出。我们从一个函数开始collect,它将递归地处理{name?, search_words?, children?, ...rest}对象,返回与给定谓词匹配的节点并在子节点上重复出现。我们用函数 来调用它,search它接受一个搜索词并从中创建一个谓词。(这里我们测试是否name或任何search_term匹配该术语;对于部分匹配、不区分大小写等,这很容易修改。)然后我们编写我提到的包装器searchLocations。它下降到节点,然后映射并组合调用每个根值.root的结果。searchconst 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}如果您想要的(听起来像是您可能想要的)与输入的结构相同,仅保留包含匹配项所需的节点,那么我们应该能够从树过滤函数开始执行类似的操作。假期后我会尝试看看。更新我确实又看了一遍,希望将树作为一棵树来过滤。代码并没有那么难。但这一次,我确实使用了一个convert函数将您的数据转换为更一致的递归结构。因此,整个对象变成一个数组,根部有两个元素,一个元素为name“欧洲”,另一个元素为name“北美”,每个元素都有其现有children节点。这使得所有进一步的处理变得更加容易。这里有两个关键函数:第一个是通用deepFilter函数,它采用谓词和项目数组,这些项目的节点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将搜索词转换为谓词的帮助器,该谓词在名称和所有搜索词上查找(不区分大小写)部分匹配。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 functionconst deepFilter = (pred) => (xs) => xs .flatMap (({children = [], ...rest}, _, __, kids = deepFilter (pred) (children)) => pred (rest) || kids.length ? [{...rest, ...(kids.length ? {children: kids} : {})}] : [] )// helper functionsconst 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 functionconst searchLocations = (loc, locations = convert(loc)) => (term) => term.length ? deepFilter (search (term)) (locations) : locations// sample dataconst 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 dataconst mySearch = searchLocations(myData)// UI democonst 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 demorender (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. 我把这部分留给你。但它应该显示另一种过滤嵌套结构的方法。