PIPIONE
这看起来会做到这一点:const filterDeep = (pred) => (xs, kids) => xs .flatMap ( x => pred (x) ? [x] : (kids = filterDeep (pred) (x .children || [])) && kids.length ? [{... x, children: kids}] : [] )const testIncludes = (condition) => (obj) => Object .entries (condition) .every ( ([k, v]) => (obj [k] || '') .toLowerCase () .includes (v .toLowerCase ()) )const filterMatches = (obj, conditions) => filterDeep (testIncludes (conditions)) (obj)const input = [{label: "Home", key: "home", level: 1, children: [{label: "Indoor Furniture", key: "furniture", level: 2, children: [{label: "Chair", key: "chair", level: 3}, {label: "Table", key: "table", level: 3}, {label: "Lamp", key: "lamp", level: 3}]}]}, {label: "Outdoor", key: "outdoor", level: 1, children: [{label: "Outdoor Furniture", key: "furniture", level: 2, children: [{label: "Trampoline", key: "trampoline", level: 3}, {label: "Swing", key: "swing", level: 3}, {label: "Large sofa", key: "large sofa", level: 3}, {label: "Medium Sofa", key: "mediumSofa", level: 3}, {label: "Small Sofa Wooden", key: "smallSofaWooden", level: 3}]}, {label: "Games", key: "games", level: 2, children: []}]}, {label: "Refurbrished Items", key: "refurbrished items", level: 1, children: []}, {label: "Indoor", key: "indoor", level: 1, children: [{label: "Electicity", key: "electicity", level: 2, children: []}, {label: "Living Room Sofa", key: "livingRoomSofa", level: 2, children: []}]}]console .log ('sofa:', filterMatches (input, {label: 'sofa'}))console .log ('furniture:', filterMatches (input, {label: 'furniture'})).as-console-wrapper {max-height: 100% !important; top: 0}我们分离出递归过滤机制以及对象匹配部分,将它们重新放在一起filterMatches。这个想法是,我们可能想通过多种方式进行过滤,因此该函数采用可以测试当前节点的任意谓词函数。testIncludes接受一个键值对对象并返回一个函数,该函数接受一个对象并报告该对象的相应键是否均包含相关值。(我根据您的输入/请求的输出组合在此处添加了不区分大小写的检查。)请注意,我用单词filter而不是 来命名中心函数find,因为find通常意味着返回第一个匹配项,而filter应该返回所有匹配项。为了我自己的使用,我会稍微不同地构造 main 函数:const filterMatches = (conditions) => (obj) => filterDeep (testIncludes (conditions)) (obj)console .log ('sofa:', filterMatches ({label: 'sofa'}) (input))我非常喜欢这些柯里化函数,并且按照这个顺序的参数,我觉得它们是最有用的。但是YMMV。更新评论指出主要功能出现 lint 故障。这是可以理解的,因为这在条件表达式内使用赋值时做了一些棘手的事情。所以这里有一些工作变体:将分配移至默认参数:const filterDeep = (pred) => (xs, kids) => xs .flatMap ( (x, _, __, kids = filterDeep (pred) (x .children || [])) => pred (x) ? [x] : kids.length ? [{... x, children: kids}] : [] )优点:这使我们的仅表达风格保持活力,并避免了上述的棘手问题。很容易阅读缺点:它使用默认参数,这有其问题。flatMat它需要从(Here_和__.)中命名两个未使用的参数使用声明样式:const filterDeep = (pred) => (xs, kids) => xs .flatMap ((x) => { if (pred (x)) { return [x] } const kids = filterDeep (pred) (x .children || []) if (kids.length > 0) { return [{... x, children: kids}] } return [] })优点:不再有任何形式的棘手更适合初学者缺点:if与使用纯表达式相比, and returnare语句和语句导致的模块化代码较少。使用call辅助函数:const call = (fn, ...args) => fn (...args)const filterDeep = (pred) => (xs, kids) => xs .flatMap ( (x) => pred (x) ? [x] : call ( (kids) => kids.length ? [{... x, children: kids}] : [], filterDeep (pred) (x .children || []) ) )优点:辅助函数call非常有用,并且可以在很多地方重用。它避免了任何参数的摆弄缺点:这将真正的三部分测试(returning [x]、returning[{... x, children: kids}]和returning [])的最后两个子句合并到一个函数中我对最后一个版本有一点偏好。但他们中的任何一个都可以。