Skip to main content
  1. About
  2. For Teams
Asked
Viewed 900 times
0

I have an infinity deep array tree:

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: [],
      },
    ],
  },
];

What I want to do is pass in a folderId and changes the "isSettingPermission" from false to true of the folder and its children.

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;
};

This is where I'm stuck at, when I pass in the root folderId, it gives me back the correct tree-array. But when I pass in a folderId further down the tree, result will give me null.

const result = findFolder(folderData, '1');
const result = findFolder(folderData, '2.1'); // this returns null
3
  • Does this answer your question? How to do recursive tree array function?
    tao
    –  tao
    2021-08-19 00:25:08 +00:00
    Commented Aug 19, 2021 at 0:25
  • Hi, that does but, that is using a different recursion
    webber
    –  webber
    2021-08-19 00:27:16 +00:00
    Commented Aug 19, 2021 at 0:27
  • I was stuck on trying to improve the original, with setting the state.
    webber
    –  webber
    2021-08-19 00:29:51 +00:00
    Commented Aug 19, 2021 at 0:29

2 Answers 2

1

I think the cleanest way to do this is to alter changeChildrenPermission slightly so that it works on a single node and recursively on all its children, rather than the current version that accepts an array of nodes.

Then our main function can map over our list of elements and if we're at a matching id, call changeChildrenPermission on it, otherwise recursively updating the children. It might look like this:

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}

But, if the existing changeChildrenPermission is useful to you as is for another purpose, it's not too hard to change updateFolder to use it. Just wrap the found node in an a single-element array before calling the function, and unwrap it after the return:

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}

Note the small change between the two versions:

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

But we might want to be able to reuse the ideas behind changeChildrenPermission in a more generic manner. Here's a version that abstracts out the name of the field to change and the new value to save:

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}

And finally, we could do a similar abstraction on the main function, passing in an arbitrary predicate to determine which nodes to alter and a transformation function to convert those ones:

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}

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much Scott for putting in so much effort to answer my question :). I will explore your answer.
0

Your code has multiple problems:

  • you don't have a childCount prop
  • the folders.folderId will always be undefined and can never equal id, because the array doesn't have a folderId. Only its members have this property.

Here's a working version of what you're trying to achieve. Note your code mutates the array (which is probably what you want it to do). But this means you need to run each test on a fresh copy of the initial data, because otherwise you're changing the initial data and the second test will start with data already mutated by first test.
To get around this, I wrote a basic clone function.

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);

Comments

Your Answer

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.