【问题标题】:Recursive Filter on Nested array嵌套数组上的递归过滤器
【发布时间】:2020-02-25 03:42:42
【问题描述】:

我需要根据查询过滤一个看起来像这样的嵌套结构。我需要返回与对象名称中的查询字符串匹配的所有对象,包括子树的父对象。请帮忙,我卡住了。

[
   {
     name: 'bob',
     type: 1,
     children: [
       {
         name: 'bob',
         type: 2,
         children: [
           {
             name: 'mike',
             type: 3,
             children: [ 
               {
                 name:'bob',
                 type: 7,
                 children: []
               },
               {
                 name: 'mike',
                 type: 9,
                 children: []
               }
             ]
           }
         ]
       },
       {
         name: 'mike',
         type: 2
       }
     ]
   }
 ]

现在我能够递归地在树中找到匹配项,但是该函数在第一次匹配时返回对象,并且不会在同一对象的子级别上进行更深入的搜索。有什么建议,我如何修改代码以递归搜索所有级别?

  return tree.map(copy).filter(function filterNested(node) {
    if (node.name.toLowerCase().indexOf(query) !== -1) {
      return true;
    }

    if (node.children) {
      return (node.children = node.children.map(copy).filter(filterNested))
        .length;
    }
  });

如果我正在搜索查询“bob”,预期的结果应该是,

 const arr = [
   {
     name: 'bob',
     type: 1,
     children: [
       {
         name: 'bob',
         type: 2,
         children: [
           {
             name: 'mike',
             type: 3,
             children: [ 
               {
                 name:'bob',
                 type: 7
               },
  
             ]
           }
         ]
       },
     ]
   }
 ]

【问题讨论】:

    标签: javascript arrays recursion filter


    【解决方案1】:

    了解mutual recursioncontinuation passing style的好时机

    const data =
      [{name:'bob',type:1,children:[{name:'bob',type:2,children:[{name:'mike',type:3,children:[ {name:'bob',type:7,children:[]},{name:'mike',type:9,children:[]}]}]},{name:'mike',type:2}]}]
    
    const identity = x => x
    
    const search = (all = [], query = identity, pass = identity) =>
      all.flatMap(v => search1(v, query, pass))
    
    const search1 = (one = {}, query = identity, pass = identity) =>
      query(one)
        ? pass([ { ...one, children: search(one.children, query) } ])
        : search
            ( one.children
            , query
            , children =>
                children.length === 0
                  ? pass([])
                  : pass([ { ...one, children } ])
            )
    
    const result =
      search(data, x => x.name === "bob")
    
    console.log(JSON.stringify(result, null, 2))

    【讨论】:

      【解决方案2】:

      我确实认为这个问题总是相关的,所以这就是我的做法! 几个小时后;)

      var res = yourArray.filter(function f(el) {
          if (el.children.length > 0) {
              el.children = el.childreb.filter(f);
          }
          if (el.name === "bob") return true; // you can put the condition you need here.
      })
      //res is your returned array
      

      真心希望这段代码能让你们中的一些人受益:)

      【讨论】:

      • 但请注意,OP 希望在 bob 对象中也包含 bob。这种技术需要更复杂才能处理。
      【解决方案3】:

      如果它们的长度不为零,您可以减少数组并使用可选的子对象构建新对象。

      function filter(array, fn) {
          return array.reduce((r, o) => {
              var children = filter(o.children || [], fn);
              if (fn(o) || children.length) r.push(Object.assign({}, o, children.length && { children }));
              return r;
          }, []);
      }
      
      var data = [{ name: 'bob', type: 1, children: [{ name: 'bob', type: 2, children: [{ name: 'mike', type: 3, children: [{ name: 'bob', type: 7 }, { name: 'same', typ: 9 }] }] }, { name: 'mike', type: 2 }] }],
          result = filter(data, ({ name }) => name.toLowerCase() === 'bob');
      
      console.log(result);
      .as-console-wrapper { max-height: 100% !important; top: 0; }

      【讨论】:

      • 哦,Object.assign({}, o, children.length && { children }) 真是个绝妙的技巧。荣誉。
      • 非常感谢 Scholz 夫人。你太聪明了。能否请您介绍一下您的功能是如何工作的?
      • @YordanRadev,由于是一种递归方法,您可以先查看停止递归的退出条件。这里 i 隐藏在空数组o.children || [] 中。这可以防止永远运行,因为带有 reduce 的空数组返回 startValue
      • 考虑到这一点,以下部分是直截了当的。该函数有两个参数,一个是数据数组,另一个是具有条件的函数,该对象包含具有特定值的所需属性。然后它获取过滤后的子对象并检查对象或子对象的长度,然后才将新对象添加到结果集中。
      【解决方案4】:

      我只会使用良好的旧访问者模式来遍历并构建新树。

      class Visitor {
          constructor(predicate) {
              this.predicate = predicate;
          }
      
          visit(item) {
              if (Array.isArray(item)) {
                  return this.visitArray(item);
              } else if (item) {
                  return this.visitItem(item);
              }
          }
      
          visitArray(item) {
              const result = [];
              for (let e of item) {
                  const item = this.visit(e);
                  if (item) {
                      result.push(item);
                  }
              }
              return result;
          }
      
          visitItem(item) {
              const children = this.visit(item.children);
              const hasChildren = (children && children.length > 0);
      
              if (hasChildren || this.predicate(item)) {
                  return {
                      name: item.name,
                      type: item.type,
                      children: children
                  }
              }
              return null;
          }
      }
      
      
      const visitor = new Visitor((item) => item.name === "bob");
      const result = visitor.visit(data);
      
      console.log(result);
      

      【讨论】:

        【解决方案5】:

        您可以为此使用递归减少,在累积对象之前检查是否有任何子项或名称是否匹配:

        const example = [{
          name: 'bob',
          type: 1,
          children: [{
              name: 'bob',
              type: 2,
              children: [{
                name: 'mike',
                type: 3,
                children: [{
                    name: 'bob',
                    type: 7,
                    children: []
                  },
                  {
                    name: 'mike',
                    type: 9,
                    children: []
                  }
                ]
              }]
            },
            {
              name: 'mike',
              type: 2
            }
          ]
        }];
        
        function reduceName(accum, item, matcher) {
          item.children = (item.children || []).reduce((a,i)=>reduceName(a,i,matcher),[]);
          if (!item.children.length) delete item.children;
          if (matcher(item) || item.children) accum.push(item);
          return accum;
        }
        
        console.log(example.reduce((a,i)=>reduceName(a,i,x=>x.name==='bob'),[]));

        【讨论】:

          【解决方案6】:

          大概是这样的吧?

          function nestedFilter(query, nodes) {
            return nodes.reduce((result, node) => {
              const filteredChildren = node.children && nestedFilter(query, node.children);
              const nameMatches = node.name.toLowerCase().indexOf(query) !== -1;
              const childrenMatch = filteredChildren && filteredChildren.length;
              const shouldKeep = nameMatches || childrenMatch;
              return shouldKeep 
                ? result.concat({ ...node, children: filteredChildren }) 
                : result;    
            }, []);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-07-27
            • 2019-09-15
            • 1970-01-01
            • 2017-01-28
            • 2019-07-19
            • 1970-01-01
            • 2022-08-19
            相关资源
            最近更新 更多