【问题标题】:Find only questions in nested array (recursive)仅查找嵌套数组中的问题(递归)
【发布时间】:2021-12-17 18:48:50
【问题描述】:

我试图在这个深层嵌套数组中找到类型等于 question 的所有对象:

{
  "uuid": "707a5ffd-68e2-4dbd-b539-128512ba3a0a",
  "type": "page",
  "items": [
    {
      "uuid": "9d823429-cc24-444d-a21c-a81357305851",
      "title": "1",
      "type": "question",
    },
    {
      "type": "section",
      "title": "2",
      "uuid": "346dec94-124c-4932-bd40-af9dc68f1d27",
      "items": [
        {
          "uuid": "bf0a9ab9-99cc-4833-b3d3-84a97072e85f",
          "title": "2.1",
          "type": "question",
        }
      ],
    },
    {
      "type": "section",
      "title": "3",
      "uuid": "4964096d-0de9-4ab1-ace5-e42516d6b866",
      "items": [
        {
          "uuid": "b2170580-1e2e-4fb4-a7b9-a56b79db21b3",
          "title": "3.1",
          "type": "question",
        }
      ],
    }
  ],
  "params": {
    "collapsed": false
  }
}

到目前为止,我有以下算法来遍历深度数组:

export const findQuestions = (items, item = undefined, questions = []) => {
  const _questions = [...questions];

  if (
    item !== undefined
    && !Object.prototype.hasOwnProperty.call(item, 'items')
  ) {
    return _questions;
  }

  for (const _item of items) {
    if (_item.type === 'question') {
      _questions.push(_item);
    }

    if (Object.prototype.hasOwnProperty.call(_item, 'items')) {
      return findQuestions(_item.items, _item, _questions);
    }
  }

  return _questions;
};

我通过传递上面引用的对象来调用findQuestions

const questions = findQuestions([this.lastPage]);

不幸的是,算法在2.1 处停止,并且没有横向到3.1。我的目标是拥有一个包含所有 questions 的数组:

[
  {
    "title": "1",
  },
  {
    "title": "2.1",
  },
  {
    "title": "3.1",
  }
]

我假设问题出在

if (
    item !== undefined
    && !Object.prototype.hasOwnProperty.call(item, 'items')
  ) {
    return _questions;
  }

它比它应该更快地返回。如何获取所有questions

【问题讨论】:

    标签: javascript arrays recursion


    【解决方案1】:

    另一种方法是在可重用函数的基础上构建它,您可能会保留这些函数以满足其他需求。在这里,我们将getQuestionTitles 构建为对findByType ('question') 的简单调用,并从匹配节点中提取标题。 findByType 是使用findByType 提供的谓词(o) => o .type == 'question'deepFilter 的调用。并且deepFilter 调用traverse 来访问对象中的所有节点和递归子节点(深度优先预排序;后排序将是一个微不足道的变化。)

    function * traverse (o) {
      yield o;
      if (Object (o) === o) {
        for (let c of Object .values (o)) {yield * traverse (c)}
      }
    }
    
    const deepFilter = (pred) => (o) => 
      [... traverse (o)] .filter (pred)
    
    const findByType = (target) => 
      deepFilter ((o) => o .type == target)
    
    const getQuestionTitles = (obj) =>
      findByType ('question') (obj) .map (({title}) => ({title}))
    
    const data = {uuid: "707a5ffd-68e2-4dbd-b539-128512ba3a0a", type: "page", items: [{uuid: "9d823429-cc24-444d-a21c-a81357305851", title: "1", type: "question"}, {type: "section", title: "2", uuid: "346dec94-124c-4932-bd40-af9dc68f1d27", items: [{uuid: "bf0a9ab9-99cc-4833-b3d3-84a97072e85f", title: "2.1", type: "question"}]}, {type: "section", title: "3", uuid: "4964096d-0de9-4ab1-ace5-e42516d6b866", items: [{uuid: "b2170580-1e2e-4fb4-a7b9-a56b79db21b3", title: "3.1", type: "question"}]}], params: {collapsed: false}}
    
    console .log (getQuestionTitles (data))

    虽然getQuestionTitles 专门针对这个问题,但其他的则越来越通用。 traverse 是作为生成器构建的,因为我们可以将它用于 findfilter,并且我们希望在它完成后立即停止。

    我发现这是一种非常有用的编码风格:保留通用抽象并在其上编写我的特定代码,从而使自定义代码非常简单。

    【讨论】:

      【解决方案2】:

      这是一个简单的递归函数

      const data = {
        "uuid": "707a5ffd-68e2-4dbd-b539-128512ba3a0a",
        "type": "page",
        "items": [
          {
            "uuid": "9d823429-cc24-444d-a21c-a81357305851",
            "title": "1",
            "type": "question",
          },
          {
            "type": "section",
            "title": "2",
            "uuid": "346dec94-124c-4932-bd40-af9dc68f1d27",
            "items": [
              {
                "uuid": "bf0a9ab9-99cc-4833-b3d3-84a97072e85f",
                "title": "2.1",
                "type": "question",
              }
            ],
          },
          {
            "type": "section",
            "title": "3",
            "uuid": "4964096d-0de9-4ab1-ace5-e42516d6b866",
            "items": [
              {
                "uuid": "b2170580-1e2e-4fb4-a7b9-a56b79db21b3",
                "title": "3.1",
                "type": "question",
              }
            ],
          }
        ],
        "params": {
          "collapsed": false
        }
      }
      
      const getQuestions = (data) => {
        let questions = []
        data.items.map(d => {
          if(d.type == 'question') {
            questions.push({type: d.type, uutd: d.uuid, title: d.title})
           }
           if(d.items) {
              questions = [...questions, ...getQuestions(d)]
           }
        })
        return questions;
      }
      
      console.log(getQuestions(data))

      【讨论】:

        【解决方案3】:

        您可以使用递归函数返回问题并递归地遍历部分:

        function getQuestions(items) {
          return items.flatMap(({ type, title, items }) => type === 'question' ? { title } : getQuestions(items));
        }
        

        例子:

        const data = {
          "uuid": "707a5ffd-68e2-4dbd-b539-128512ba3a0a",
          "type": "page",
          "items": [
            {
              "uuid": "9d823429-cc24-444d-a21c-a81357305851",
              "title": "1",
              "type": "question",
            },
            {
              "type": "section",
              "title": "2",
              "uuid": "346dec94-124c-4932-bd40-af9dc68f1d27",
              "items": [
                {
                  "uuid": "bf0a9ab9-99cc-4833-b3d3-84a97072e85f",
                  "title": "2.1",
                  "type": "question",
                }
              ],
            },
            {
              "type": "section",
              "title": "3",
              "uuid": "4964096d-0de9-4ab1-ace5-e42516d6b866",
              "items": [
                {
                  "uuid": "b2170580-1e2e-4fb4-a7b9-a56b79db21b3",
                  "title": "3.1",
                  "type": "question",
                }
              ],
            }
          ],
          "params": {
            "collapsed": false
          }
        };
        
        function getQuestions(items) {
          return items.flatMap(({ type, title, items }) => type === 'question' ? { title } : getQuestions(items));
        }
        
        console.log(getQuestions(data.items));

        【讨论】:

        • 请注意,这仅在问题始终是叶节点时才有效。如果他们也可以有问题孩子,那么这种技术是不够的。
        • @ScottSauyet 我假设问题是叶节点。部分用于分组。
        • 如果是这种情况,这是一个很好的解决方案,我怀疑它是。我只是想注意这个假设。没有做出这种假设的调整:return items.flatMap(({ type, title, items = [] }) => [...(type === 'question' ? [{ title }] : []), ...getQuestions(items)]).
        【解决方案4】:

        递归解决方案:

        您可以使用type as question as 获取所有对象:

        function getItems(obj) {
          if (obj.type === "question") result.push({ title: obj.title });
          if (obj.items) obj.items.forEach((o) => getItems(o));
        }
        

        const obj = {
          uuid: "707a5ffd-68e2-4dbd-b539-128512ba3a0a",
          type: "page",
          items: [
            {
              uuid: "9d823429-cc24-444d-a21c-a81357305851",
              title: "1",
              type: "question",
            },
            {
              type: "section",
              title: "2",
              uuid: "346dec94-124c-4932-bd40-af9dc68f1d27",
              items: [
                {
                  uuid: "bf0a9ab9-99cc-4833-b3d3-84a97072e85f",
                  title: "2.1",
                  type: "question",
                },
              ],
            },
            {
              type: "section",
              title: "3",
              uuid: "4964096d-0de9-4ab1-ace5-e42516d6b866",
              items: [
                {
                  uuid: "b2170580-1e2e-4fb4-a7b9-a56b79db21b3",
                  title: "3.1",
                  type: "question",
                },
              ],
            },
          ],
          params: {
            collapsed: false,
          },
        };
        
        const result = [];
        function getItems(obj) {
          if (obj.type === "question") result.push({ title: obj.title });
          if (obj.items) obj.items.forEach((o) => getItems(o));
        }
        
        getItems(obj);
        console.log(result);

        【讨论】:

          【解决方案5】:

          我向你推荐这个解决方案

          function findQuestions(array) {
                const questions = [];
                for (const element of array) {
                  if (element.type === "question") {
                    questions.push(element)
                  }
          
                  if ("items" in element) {
                    questions.push(findQuestions(element.items))
                  }
                }
          
                return questions
          }
          

          【讨论】:

            【解决方案6】:

            如果你有不止一层的嵌套,那么尝试使用递归方法:

            let allItems = [];
            
            const traverseObject = (obj) => {
                if (!obj.items || obj.items.length == 0)
                    return;
            
                for (let item of obj.items)
                {
                  if (item.type === 'question')
                      allItems.push(item);
                  
                  if (item.items && item.items.length > 0)
                      traverseObject(item)
                }
            }
            

            一个例子:

            const fooJson = {
              "uuid": "707a5ffd-68e2-4dbd-b539-128512ba3a0a",
              "type": "page",
              "items": [
                {
                  "uuid": "9d823429-cc24-444d-a21c-a81357305851",
                  "title": "1",
                  "type": "question",
                },
                {
                  "type": "section",
                  "title": "2",
                  "uuid": "346dec94-124c-4932-bd40-af9dc68f1d27",
                  "items": [
                    {
                      "uuid": "bf0a9ab9-99cc-4833-b3d3-84a97072e85f",
                      "title": "2.1",
                      "type": "question",
                      "items": [
                        {
                          "uuid": "bf0a9ab9-99cc-4833-b3d3-84a97072e85f",
                          "title": "fooBar",
                          "type": "question",
                        }
                      ],
                    }
                  ],
                },
                {
                  "type": "section",
                  "title": "3",
                  "uuid": "4964096d-0de9-4ab1-ace5-e42516d6b866",
                  "items": [
                    {
                      "uuid": "b2170580-1e2e-4fb4-a7b9-a56b79db21b3",
                      "title": "3.1",
                      "type": "question",
                    }
                  ],
                }
              ],
              "params": {
                "collapsed": false
              }
            };
            
            
            
            let allItems = [];
            
            const traverseObject = (obj) => {
                if (!obj.items || obj.items.length == 0)
                    return;
            
                for (let item of obj.items)
                {
                  if (item.type === 'question')
                      allItems.push(item);
                  
                  if (item.items && item.items.length > 0)
                      traverseObject(item)
                }
            }
            
            traverseObject(fooJson);
            console.log(allItems)

            【讨论】:

              【解决方案7】:

              您可以使用递归生成器函数:

              function* get_titles(d){ 
                  if (d.type === 'question'){
                      yield {title:d.title}
                  }
                  for (var i of ('items' in d ? d.items : [])){
                      yield* get_titles(i)
                  }
              }
              var result = [...get_titles(data)]
              

              【讨论】:

                猜你喜欢
                • 2018-02-14
                • 1970-01-01
                • 2021-05-06
                • 1970-01-01
                • 2012-08-22
                • 2020-03-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多