【问题标题】:How to do recursion for tree array?如何对树数组进行递归?
【发布时间】:2021-10-20 17:39:40
【问题描述】:

我有一个无限深的数组树:

const folderData = [
  {
    folderId: '1',
    isSettingPermission: false,
    children: [
      {
        folderId: '2.1',
        isSettingPermission: false,
        children: [
          {
            folderId: '3.1',
            isSettingPermission: false,
            children: [],
          },
        ],
      },
      {
        folderId: '2.2',
        isSettingPermission: false,
        children: [],
      },
    ],
  },
];

我想要做的是传入一个 folderId 并将文件夹及其子文件夹的“isSettingPermission”从 false 更改为 true。

const changeChildrenPermission = (folder) =>
  folder.map(({ folderId, children }) => ({
    folderId,
    isSettingPermission: true,
    children: changeChildrenPermission(children),
  }));

export const findFolder = (
  folders,
  id
) => {
  let res = null;
  if (folders.folderId === id) {
    res = changeChildrenPermission([folders]);
  }

  for (let i = 0; i < folders.children.length; i++) {
    findFolder(folders.children[i], folders.children[i].folderId);
  }
  return res;
};

这是我遇到的问题,当我传入根文件夹 ID 时,它会返回正确的树数组。但是当我在树的下方传递一个 folderId 时,结果会给我 null。

const result = findFolder(folderData, '1');
const result = findFolder(folderData, '2.1'); // this returns null

【问题讨论】:

标签: javascript reactjs recursion tree


【解决方案1】:

我认为最干净的方法是稍微改变changeChildrenPermission,使其在单个节点上工作并递归地在其所有子节点上工作,而不是接受节点数组的当前版本。

然后我们的 main 函数可以映射我们的元素列表,如果我们的 id 匹配,则在其上调用changeChildrenPermission,否则递归更新子元素。它可能看起来像这样:

const changeChildrenPermission = ({folderId, children}) => ({
  folderId,
  isSettingPermission: true,
  children: children .map (changeChildrenPermission)
})

const updateFolder = (id) => (xs) => 
  xs .map (x => x .folderId == id 
    ? changeChildrenPermission (x)
    : {... x, children: updateFolder (id) (x.children)}
  )

const folderData = [{folderId: '1', isSettingPermission: false, children: [{folderId: '2.1', isSettingPermission: false, children: [{folderId: '3.1', isSettingPermission: false, children: []}]}, {folderId: '2.2', isSettingPermission: false, children: []}]}];

console .log (updateFolder ('2.1') (folderData))
.as-console-wrapper {max-height: 100% !important; top: 0}

但是,如果现有的changeChildrenPermission 对您有用,并且用于其他目的,那么更改updateFolder 以使用它并不难。只需在调用函数之前将找到的节点包装在一个单元素数组中,并在返回后将其解包:

const changeChildrenPermission = (folder) =>
  folder .map (({folderId, children}) => ({
    folderId,
    isSettingPermission: true,
    children: changeChildrenPermission (children),
  }));

const updateFolder = (id) => (xs) => 
  xs .map (x => x .folderId == id 
    ? changeChildrenPermission ([x]) [0]
    : {... x, children: updateFolder (id) (x.children)}
  )

const folderData = [{folderId: '1', isSettingPermission: false, children: [{folderId: '2.1', isSettingPermission: false, children: [{folderId: '3.1', isSettingPermission: false, children: []}]}, {folderId: '2.2', isSettingPermission: false, children: []}]}];

console .log (updateFolder ('2.1') (folderData))
.as-console-wrapper {max-height: 100% !important; top: 0}

注意两个版本之间的微小变化:

-    ? changeChildrenPermission (x)
+    ? changeChildrenPermission ([x]) [0]

但我们可能希望能够以更通用的方式重用 changeChildrenPermission 背后的想法。这是一个抽象出要更改的字段名称和要保存的新值的版本:

const updateNestedField = (name, value) => (node) => ({
  ... node,
  [name]: value,
  children: node .children .map (updateNestedField (name, value))
})

const updateFolder = (id) => (xs) => 
  xs .map (x => x .folderId == id 
    ? updateNestedField ('isSettingPermission', true) (x)
    : {... x, children: updateFolder (id) (x.children)}
  )

const folderData = [{folderId: '1', isSettingPermission: false, children: [{folderId: '2.1', isSettingPermission: false, children: [{folderId: '3.1', isSettingPermission: false, children: []}]}, {folderId: '2.2', isSettingPermission: false, children: []}]}];

console .log (updateFolder ('2.1') (folderData))
.as-console-wrapper {max-height: 100% !important; top: 0}

最后,我们可以对 main 函数进行类似的抽象,传入一个任意谓词来确定要更改哪些节点以及转换这些节点的转换函数:

const updateNestedField = (name, value) => (node) => ({
  ... node,
  [name]: value,
  children: node .children .map (updateNestedField (name, value))
})

const updateList = (pred, transform) => (xs) =>
  xs .map (x => pred (x) ? transform (x) : {... x, children: updateList (pred, transform) (x .children)})

const updateFolder = (id) => updateList (
  x => x .folderId == id,
  updateNestedField ('isSettingPermission', true)
)

const folderData = [{folderId: '1', isSettingPermission: false, children: [{folderId: '2.1', isSettingPermission: false, children: [{folderId: '3.1', isSettingPermission: false, children: []}]}, {folderId: '2.2', isSettingPermission: false, children: []}]}];

console .log (updateFolder ('2.1') (folderData))
.as-console-wrapper {max-height: 100% !important; top: 0}

【讨论】:

  • 非常感谢 Scott 付出了这么多努力来回答我的问题 :)。我会探索你的答案。
【解决方案2】:

您的代码有多个问题:

  • 你没有 childCount 属性
  • folders.folderId 将始终为undefined,并且永远不能等于id,因为数组没有folderId。只有它的成员才有这个属性。

这是您尝试实现的工作版本。请注意,您的代码会改变数组(这可能是您希望它执行的操作)。但这意味着您需要在初始数据的新副本上运行每个测试,否则您将更改初始数据,并且第二个测试将从第一个测试已经发生变异的数据开始。
为了解决这个问题,我编写了一个基本的clone 函数。

const folderData = [
  {
    folderId: '1',
    isSettingPermission: false,
    children: [
      {
        folderId: '2.1',
        isSettingPermission: false,
        children: [
          {
            folderId: '3.1',
            isSettingPermission: false,
            children: []
          },
        ],
      },
      {
        folderId: '2.2',
        isSettingPermission: false,
        children: []
      },
    ],
  },
];

const changeChildrenPermission = ({ folderId, children }) => ({
  folderId,
  isSettingPermission: true,
  children: children?.map(child => changeChildrenPermission(child))
})

const findFolder = (folders, id) => {
  if (folders?.length) {
    const index = folders.findIndex(({ folderId }) => folderId === id);
    if (index > -1) {
      // if we found the folder
      folders[index] = changeChildrenPermission(folders[index]);
    } else {
      // if folder was not found, check children
      folders.forEach(({ children }) => {
        findFolder(children, id);
      })
    }
  }
};

const clone = data => JSON.parse(JSON.stringify(data));

let data;
data = clone(folderData);
findFolder(data, '1');
console.log(data);

data = clone(folderData);
findFolder(data, '2.1');
console.log(data);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-04
    • 1970-01-01
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 2021-06-22
    相关资源
    最近更新 更多