我在這里搜索了自下而上的搜索示例,并且我了解它們是如何完成的,但是我在這里有一個我無法解決的特定需求。
我們有一個選單系統,我需要為其創建過濾器。
例如,如果選單是
- Testing
- Test
- Something
- Something Else
- Test
如果我過濾“測驗”,我想要4 個節點,其他所有節點都被刪掉。棘手的部分是在這種情況下它無法洗掉“Something”節點,因為它具有需要訪問的匹配子節點。
我的代碼有效,但僅適用于頂級專案,因為filter在第一個遞回步驟中通過洗掉任何可能具有匹配子級但父級不匹配的內容。
private recursiveFilter(menuItems: MenuItem[], label: string): MenuItem[] {
if (label === '') {
this.menuItems = this.originalMenuItems;
return this.menuItems;
} else if (!menuItems) {
return [];
}
return menuItems
.filter((el) => el.label?.toLowerCase().includes(label.toLowerCase()))
.map((el) => {
if (!(el.items || !Array.isArray(el.items))) {
return el;
} else {
const menuChildren = el.items as MenuItem[];
if (menuChildren) {
el.items = this.recursiveFilter(menuChildren, label);
}
return el;
}
});
}
具有嵌套子項的 MenuItem 示例:
{
"label": "Messages",
"expanded": false,
"items": [
{
"label": "Dashboard",
"expanded": false,
"routerLink": "/messages",
"visible": true
},
{
"label": "Voicemail",
"expanded": false,
"items": [
{
"label": "Inbox",
"icon": "pi pi-fw pi-folder",
"routerLink": "/mailbox/inbox"
},
{
"label": "Archived",
"icon": "pi pi-fw pi-folder",
"routerLink": "/mailbox/archived"
},
{
"label": "Trash",
"icon": "pi pi-fw pi-folder",
"routerLink": "/mailbox/trash"
}
]
},
{
"label": "Text",
"expanded": false,
"items": [
{
"label": "Messages",
"routerLink": "/sms",
"routerLinkActiveOptions": {
"exact": true
}
},
{
"label": "Send",
"routerLink": "/sms/send",
"routerLinkActiveOptions": {
"exact": true
}
},
{
"label": "Contacts",
"routerLink": "/sms/contacts"
}
]
}
]
}
uj5u.com熱心網友回復:
在遞回呼叫確定要保留哪些孩子之后進行過濾。如果過濾后仍然存在任何子元素或標簽匹配,則保留該父項。
類似于以下內容:
private recursiveFilter(menuItems: MenuItem[], label: string, labelLower = label.toLowerCase()) {
return menuItems.filter((item) => {
if (item.items) item.items = recursiveFilter(item.items, label, labelLower);
return item.label?.toLowerCase().includes(labelLower) || item.items?.length;
});
}
a 內部的副作用.filter有點臭,但我認為這是解決這個問題的最清晰方法。
現場演示:
顯示代碼片段
const recursiveFilter = (menuItems, label) => (
menuItems.filter((item) => {
if (item.items) item.items = recursiveFilter(item.items, label);
return item.label?.toLowerCase().includes(label) || item.items?.length;
})
);
const topItem={label:"Messages",expanded:!1,items:[{label:"Dashboard",expanded:!1,routerLink:"/messages",visible:!0},{label:"Voicemail",expanded:!1,items:[{label:"Inbox",icon:"pi pi-fw pi-folder",routerLink:"/mailbox/inbox"},{label:"Archived",icon:"pi pi-fw pi-folder",routerLink:"/mailbox/archived"},{label:"Trash",icon:"pi pi-fw pi-folder",routerLink:"/mailbox/trash"}]},{label:"Text",expanded:!1,items:[{label:"Messages",routerLink:"/sms",routerLinkActiveOptions:{exact:!0}},{label:"Send",routerLink:"/sms/send",routerLinkActiveOptions:{exact:!0}},{label:"Contacts",routerLink:"/sms/contacts"}]}]};
topItem.items = recursiveFilter(topItem.items, 'send');
console.log(topItem);
uj5u.com熱心網友回復:
我更喜歡將遞回過濾從所需的特定檢查中分離出來。然后我們可以根據需要傳入一個謂詞函式。我覺得這更簡單。所以我可能會這樣寫:
const deepFilter = (pred) => ({items = [], ...rest}) => {
const children = items .flatMap (deepFilter (pred))
return (children .length || pred (rest))
? [{...rest, ...(items.length ? {items: children} : {})}]
: []
}
const matchLabel = (text) => ({label = ''}) =>
label .toLowerCase () .includes (text .toLowerCase())
const filterByLabel = (t) =>
deepFilter (matchLabel (t))
const menuItem = {label: "Messages", expanded: false, items: [{label: "Dashboard", expanded: false, routerLink: "/messages", visible: true}, {label: "Voicemail", expanded: false, items: [{label: "Inbox", icon: "pi pi-fw pi-folder", routerLink: "/mailbox/inbox"}, {label: "Archived", icon: "pi pi-fw pi-folder", routerLink: "/mailbox/archived"}, {label: "Trash", icon: "pi pi-fw pi-folder", routerLink: "/mailbox/trash"}]}, {label: "Text", expanded: false, items: [{label: "Messages", routerLink: "/sms", routerLinkActiveOptions: {exact: true}}, {label: "Send", routerLink: "/sms/send", routerLinkActiveOptions: {exact: true}}, {label: "Contacts", routerLink: "/sms/contacts"}]}]}
console .log ('labels containing "send":', filterByLabel ('send') (menuItem))
console .log ('labels containing "a":', filterByLabel ('a') (menuItem))
console .log ('labels containing "foobar":', filterByLabel ('foobar') (menuItem))
.as-console-wrapper {max-height: 100% !important; top: 0}
在這里,deepFilter接受一個謂詞函式并回傳一個函式,該函式接受一個嵌套有子節點的樹,items并僅回傳與謂詞匹配或具有與謂詞匹配的子項的那些項。我們撰寫matchLabel謂詞,它接受一個搜索字串并回傳一個函式,該函式測驗傳遞給它的物件是否具有label(不區分大小寫)包含搜索詞的屬性。最后,filterByLabel簡單地組合它們,接受一個搜索詞并回傳一個函式,該函式接受一個專案并(遞回地)回傳該專案,如果它與該詞匹配或其任何子項匹配。
變化
有些事情我們可能想要改變。
首先,是每次都
matchLabel呼叫文本。.toLowerCase我們可以用這個替代方案來避免這種情況:const matchLabel = (t, text = t.toLowerCase()) => ({label = ''}) => label .toLowerCase () .includes (text)其次,
deepFilter它很好而且通用......除了它items在函式中硬編碼陣列的樹結構。我們可能希望通過將該節點名稱作為引數來使其更通用。我們可以這樣做const deepFilter = (childName) => (pred) => ({[childName]: items = [], ...rest}) => { const children = items .flatMap (deepFilter (childName) (pred)) return (children .length || pred (rest)) ? [{...rest, ...(items.length ? {[childName]: children} : {})}] : [] }然后像這樣使用它:
const filterByLabel = (t) => deepFilter ('items') (matchLabel (t))或通過命名中間函式:
const deepFilterItemTree = deepFilter ('items') const filterByLabel = (t) => deepFilterItemTree (matchLabel (t))第三,如果我們碰巧有一個方便的
compose函式,我們可能會將 main 函式重寫為const filterByLabel = compose (deepFilter, matchLabel)或者,如果我們采用第二個建議,作為其中之一:
const filterByLabel = compose (deepFilter ('items'), matchLabel) // or const filterByLabel = compose (deepFilterItemTree, matchLabel)
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/417796.html
標籤:
