开发者问题收集

如何修复使用递归和数组时的错误?

2022-07-14
85

我正在尝试解决一个问题 - 在输入要搜索的值后需要返回一个数组。数组的深度理论上可以是无限的。因此,我递归地在任何深度级别中找到合适的元素并将它们添加到数组中,然后返回此数组。但是此代码无法按预期工作。我搞不清楚我到底做错了什么。

const newList = [
  {
      role: "role111",
      title: "title1",

  },
  {
      role: "role222",
      title: "title2",
  },
  {
      role: "role333",
      title: "title3",
  },
  {
      role: "role444",
      title: "title4",
      items: [{
          role: "role555",
          title: "title5",
      }, {
          role: "role666",
          title: "title6",
      }, {
          role: "role777",
          title: "title7",
          items: [{
              role: "role888",
              title: "title888",
          },{
              role: "role8888",
              title: "title8888",
          },]
      },]
  },
  {
      role: "role999",
      title: "title999",
  },
];

const text = "role888";


const testFunction = (
  list,
  text,
  emptyArray
) => {
  let arrayForRender = emptyArray;

  return list?.filter((item) => {
      if (item.title.toLowerCase().includes(text.toLowerCase())) {
          arrayForRender = [...arrayForRender, item];
          return arrayForRender;
      }
      if (item.items && item.items?.length > 0) {
          testFunction(item.items, text, arrayForRender);
      }
  });
};

console.log(testFunction(newList, text, []));

附注:非常抱歉,但我的问题最初表述错误,不再相关。但感谢所有提出建议的人,他们会告诉你如何正确使用递归!

2个回答

如果我理解了您更新后的要求,那么我们可以编写一个简单的通用深度过滤函数,该函数显示与谓词匹配的所有元素或具有与之匹配的(递归嵌套)后代的元素。

我们可以使用一个函数来配置它,该函数测试标题是否与查询字符串匹配。它可能看起来像这样:

const filterDeep = (pred) => (xs) =>
  xs .filter (x => pred (x) || filterDeep (pred) (x .items || []) .length > 0)

const titleMatch = (query) => filterDeep (
  ({title}) => title .toLowerCase () .includes (query .toLowerCase ())
)


const newList = [{role: "role111", title: "title1"}, {role: "role222", title: "title2"}, {role: "role333", title: "title3"}, {role: "role444", title: "title4", items: [{role: "role555", title: "title5"}, {role: "role666", title: "title6"}, {role: "role777", title: "title7", items: [{role: "role888", title: "title888"}, {role: "role8888", title: "title8888"}]}]}, {role: "role999", title: "title999"}];

['title2', '888', 'itl'] .forEach (
  query => console .log (`"${query}"`, ':', titleMatch (query) (newList) .map (x => x .title))
)
.as-console-wrapper {max-height: 100% !important; top: 0}

(请注意,结果是实际的原始节点,但这里我们只显示它们的标题。)

我认为这是简单的代码,并且 filterDeep 很可能在其他地方可用。

更新

很明显,我没有得到要求。如果我现在是正确的,并且您想要与谓词匹配的任何节点的完整对象层次结构,那么这个版本应该可以做到,它只是对 filterDeep 进行了不同的实现:

const filterDeep = (pred) => (xs) =>
  xs .flatMap (({items = [], kids = filterDeep (pred) (items), ...rest}) =>
    pred (rest) || kids.length
      ? [{...rest, ...(kids.length ? {items: kids} : {})}]
      : []
  )

const titleMatch = (query) => filterDeep (
  ({title}) => title .toLowerCase () .includes (query .toLowerCase ())
)


const newList = [{role: "role111", title: "title1"}, {role: "role222", title: "title2"}, {role: "role333", title: "title3"}, {role: "role444", title: "title4", items: [{role: "role555", title: "title5"}, {role: "role666", title: "title6"}, {role: "role777", title: "title7", items: [{role: "role888", title: "title888"}, {role: "role8888", title: "title8888"}]}]}, {role: "role999", title: "title999"}];

['title2', '888', 'itl'] .forEach (
  query => console .log (`"${query}"`, ':', titleMatch (query) (newList))
)
.as-console-wrapper {max-height: 100% !important; top: 0}

我们在这里使用 flatMap 作为一次性 filtermap 的一种方式。如果项目不匹配,我们将返回一个空数组;如果匹配,那么我们将返回一个仅包含其转换值的数组。

Scott Sauyet
2022-07-14

我认为 filter 方法在这里不起作用,而是尝试使用 forEach 方法循环遍历数组并将找到的项目推送到 arrayForRender 。我还注意到在您的代码中,您的搜索文本是一个角色,但您的条件是查看标题。

const testFunction = (
  list,
  text,
  arrayForRender
 ) => {
 list.forEach((item) => {
    if (item.items && item.items?.length > 0) {
       testFunction(item.items, text, arrayForRender);
     } 
    if (item.role.toLowerCase().includes(text.toLowerCase())) {
       arrayForRender.push(item);  
       //arrayForRender.push({'role':item.role, 'title':item.title})
     } 
  });
  return(arrayForRender)
};

console.log("search results:",testFunction(newList, text,[]));

根据您下面的评论,以下代码仅返回顶级节点(如果它与搜索匹配或其任何子节点与搜索匹配):

const testFunction = (
  list,
  text
) => {
  return list?.filter((item) => {
    if (item.items && item.items?.length > 0) {
        return testFunction(item.items, text);
    }

    return item.role.toLowerCase().includes(text.toLowerCase());
  });
};


console.log("search results:",testFunction(newList, text));
Woodsy
2022-07-14