简体   繁体   中英

Modify/Add A Array Deeply Nested Inside Object

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.

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

As @mhodges said

Your if (treeObj.full == fullPath) { return treeObj; } (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. ie { treeObj.children = []; return treeObj; } { treeObj.children = []; return treeObj; } { 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM