0

I have this Data Structure:

var tree = {
  name: "docs",
  type: "dir",
  full: "/home/docs",
  children: [
    {
      name: "folder2",
      type: "dir",
      full: "/home/docs/folder2",
      children: [
        {
          name: "file2.txt",
          type: "file",
          full: "/home/docs/folder2/file2.txt"
        }
      ]
    },
    {
      name: "file1.txt",
      type: "file",
      full: "/home/docs/file1.txt"
    }
  ]
}

This data structure represents a contents of the folder of the User, so it may vary on every user's machine.

1 thing common in this is every element represents either a file or a directory, if it's a directory it will have the property type: "dir" else it will have type: "file".

if the element is a directory it will also have a children property which will be a array of such elements.

every element also has name property like folder/file name & it has a full property which is a unique string, which defines where the folder/file is on the user's filesystem.

I have written this algorithm:

var tree = {
  name: "docs",
  type: "dir",
  full: "/home/docs",
  children: [
    {
      name: "folder2",
      type: "dir",
      full: "/home/docs/folder2",
      children: [
        {
          name: "file2.txt",
          type: "file",
          full: "/home/docs/folder2/file2.txt"
        }
      ]
    },
    {
      name: "file1.txt",
      type: "file",
      full: "/home/docs/file1.txt"
    }
  ]
}

function FindChildrenInTree(fullPath, treeObj) {
    if (treeObj.type != "dir") { return null; }
    if (treeObj.children == null) { return null }
    if (treeObj.full == fullPath) { return treeObj; }

    for (var i = 0; i < treeObj.children.length; i++) {
        if (treeObj.children[i].full == fullPath) {
            return treeObj.children[i];
        } else {
            var result = FindChildrenInTree(fullPath, treeObj.children[i]);
            if (result != null) return result;
        }
    }

    return null;
}

console.log(FindChildrenInTree("/home/docs/folder2", tree))

This algorithm finds a folder in the given tree object & returns it, but instead of return the item i have found i want to add a children property that item in the given tree.

So for example i have the above given tree, i give the algorithm the item's unique path, the tree & the children array i want to add, how will i do it, i can't find any way.

5
  • 1
    Your if (treeObj.full == fullPath) { return treeObj; } is your "success" path, meaning you found the item you were looking for. So if you want to add logic into that condition, you would do it there. i.e. { treeObj.children = []; return treeObj; } or whatever you want to do Commented Jun 16, 2022 at 17:26
  • I don't understand this: you want to add a children property? But if it was a directory, then it already has children. If it was a file, it should not get a children property. Or did you mean that you want to add an entry inside an existing children property? Can you give an example of how you would call the function, and what the expected result would be? Commented Jun 17, 2022 at 6:05
  • @trincot So Basically It's a part of a file explorer, so when a user clicks on a directory i will be reading the contents of the directory and then adding the children to that item, because it can get quite slow if i recursively read all the directories & sub-directories. Commented Jun 17, 2022 at 10:10
  • What would be the issue with the solution that mhodges proposed above? Commented Jun 17, 2022 at 10:13
  • @trincot I tried it, and it works but idk how? Commented Jun 19, 2022 at 11:09

2 Answers 2

0

I leave you this example, it may have some issues but I think you can get an idea from it. I think the best way to solve this is by playing with the bracket notations, because I mean no matter if you have an object or an array if you do something like variable[key] you will get the value, and that "key" can be the property name of an object or an index number from the array. So the 1st I would do is to create an array with those keys. Then I will iterate throw the object using those keys and just update the value, and because it is an object and the type of value is by reference, the update will impact the whole object.

English is not my main language, so I hope you can understand.

var tree = {
  name: "docs",
  type: "dir",
  full: "/home/docs",
  children: [
    {
      name: "folder2",
      type: "dir",
      full: "/home/docs/folder2",
      children: [
        {
          name: "file2.txt",
          type: "file",
          full: "/home/docs/folder2/file2.txt",
        },
      ],
    },
    {
      name: "file1.txt",
      type: "file",
      full: "/home/docs/file1.txt",
    },
  ],
};

function isObject(obj) {
  return Object.prototype.toString.call(obj) === "[object Object]";
}

function isArray(arr) {
  return Array.isArray(arr);
}

function findObjectKeyPathToUpdate(fullPath, treeObj, keys = []) {
  if (isObject(treeObj)) {
    if ("full" in treeObj && treeObj.full === fullPath) {
      return keys;
    } else {
      const currentObjectKeys = Object.keys(treeObj);
      for (let i = 0; i < currentObjectKeys.length; i++) {
        const key = currentObjectKeys[i];
        if (isObject(treeObj[key]) || isArray(treeObj[key])) {
          keys.push(key);
          const res = findObjectKeyPathToUpdate(fullPath, treeObj[key], keys);
          if (res) {
            return res;
          } else {
            keys.pop();
          }
        }
      }
    }
  } else if (isArray(treeObj)) {
    for (let i = 0; i < treeObj.length; i++) {
      const currentObj = treeObj[i];
      if (isObject(currentObj) || isArray(currentObj)) {
        keys.push(i);
        const res = findObjectKeyPathToUpdate(fullPath, currentObj, keys);
        if (res) {
          return res;
        } else {
          keys.pop();
        }
      }
    }
  }
  return null;
}

function updateObjectByKeyPathArr(keyPathArr, obj, newKey, value) {
  for (let i = 0; i < keyPathArr.length; i++) {
    const key = keyPathArr[i];
    obj = obj[key];
    if (i === keyPathArr.length - 1) {
      Object.assign(obj, { [newKey]: value });
    }
  }
}
const objKeyPathArr = findObjectKeyPathToUpdate(
  "/home/docs/folder2/file2.txt",
  tree
);

updateObjectByKeyPathArr(objKeyPathArr, tree, "newKey", {
  test: "test",
});

console.log("new", JSON.stringify(tree));
Sign up to request clarification or add additional context in comments.

3 Comments

BTW you can do the update in the findObjectKeyPathToUpdate function, I did it in two steps so that it is not very complex, and it is easier to understand.
Array.isArray() has been standard for a long time. No need to reimplement.
You are right, I forgot about that xD
-1

As @mhodges said

Your if (treeObj.full == fullPath) { return treeObj; } is your "success" path, meaning you found the item you were looking for. So if you want to add logic into that condition, you would do it there. i.e. { treeObj.children = []; return treeObj; } or whatever you want to do

Works, but i don't understand how, because everytime the function is calls itself, the item i passed is modified and not the whole tree.

2 Comments

The OP should start gaining knowledge about JavaScript's mutable types and immutable values and object references.
New Thing I Learned @PeterSeliger

Your Answer

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.