0

I know that eval is bad and all but I can't really find any better way of doing this.

I receive an input string (from a command prompt) and I need to modify some value of a nested item inside a stored JSON object representing a file system.

Here it is in more detail:

I am making an edit command for my command prompt, and its form is like this:

edit <filename>

My code receives <filename> and looks in a file system for it, to check its existence.

Here is an example file system (in JSON, Linux based):

{
  "home": {
    "default": {
      "documents": {},
      "tst": "This is a file"
    }
  }
  "etc": {}
  "tmp": {}
}

So if the user's PWD (present working directory) is /home, and they enter default/tst, they should be editing /home/default/tst.

This is how I tried:

var wdir=JSON.parse(localStorage.getItem("filesys")); // Whole dir (wdir) - Obtain the stored file system
var cdir=wdir; // Current dir (cdir)
var i; // loop var
var l=f.length; // f is something like ["default","tst"]
var runstr="wdir"; // The thing to eval

for(i=0;i<l-1;i++){ // Go through everything except the last (the last is the file)
    if(f[i] in cdir){ // If it exists in the current dir
        cdir=cdir[f[i]]; // Go into a directory and modify cdir, not wdir
        runstr+="['"+f[i]+"']"; // Add more to runstr
    }
}

cdir[n]=t.value; // t.value is the new file content

eval(runstr+'=cdir;'); // run runstr to modify value

localStorage.setItem("filesys",JSON.stringify(wdir)); // Save the file system

Side note: this works, I'm just looking for a better solution

I've heard that eval is 'evil' and also slow as the JS interpreter has to start again. Since I'm using it for such a simple task I thought that there must be another way.

What better way can I set the value of a key that could be nested inside objects inside objects?

If you need any more information, you can ask in the comments and I will add it to my question.

Lakshya Raj
  • 1,669
  • 3
  • 10
  • 34
  • You want to alter the value in the object? why are you building up a string when you have a reference to the property in the code? – epascarello Mar 26 '21 at 00:24
  • `cdir[n]=t.value;` sets the value.... there should be zero reason why you need eval – epascarello Mar 26 '21 at 00:27
  • @epascarello: But `cdir` gets cut each time, so it won't be the *whole* directory. That's why I'm modifying `wdir` with `eval`. – Lakshya Raj Mar 26 '21 at 00:30
  • You walk the tree and you set the value. It will be that value. So wdir will have the new value. `console.log(JSON.stringify(wdir));` and see what it is. – epascarello Mar 26 '21 at 00:30
  • 2
    https://stackoverflow.com/questions/13719593/how-to-set-object-property-of-object-property-of-given-its-string-name-in-ja – epascarello Mar 26 '21 at 00:36

1 Answers1

1

Fun to code, the below snippet should do the trick ! You might need to add some additional conditions in order to prevent your user to override a folder ! I used the spread operator to do a "deep copy" of the object, if you want to directly alter the object passed to the function you can remove the first line and replace deepCopy by obj in the reducer. :)

const setObjectPropertyAtPath = (obj, path, value) => {
  // we do a deep copy of the object because we want to alter this copy and not directly the object passed to the   // function. Doing it provide us a way to return the modified object instead of direcly altering the original     // object
  const deepCopy = {...obj}
  path.reduce((acc, cur, level) => {
    if(level === path.length - 1) {
      acc[cur] = value;
      return value;
    }
    return acc[cur];
  }, deepCopy)
  
  return deepCopy;
}

const fs = {
  "home": {
    "default": {
      "documents": {},
      "tst": "This is a file"
    }
  },
  "etc": {},
  "tmp": {}
}

const updatedFs = setObjectPropertyAtPath(fs,["home", "default", "tst"], "foo")
console.log(updatedFs)
Zerowiel
  • 170
  • 7
  • Thanks for this answer! It really helped me! Just one question - can we do some similar process for deleting a key? I don't understand `reduce` that much, sorry. – Lakshya Raj Mar 26 '21 at 01:01
  • Yes of course instead of 'acc[cur] = value' you can simply do 'delete acc[cur]' , reduce seems difficult at first glance but you really should read the documentation and try it yourself :) – Zerowiel Mar 26 '21 at 08:51
  • I tried that and it worked. The reason I didn't respond earlier is because I was figuring out how to make it work. Turns out when I do the `delete` command, then `f` is not what I want, so I adjusted it a bit and it worked! I think I can make it also do the same for similar commands such as `create` using my new knowledge of `reduce`. Thanks for all of your support! – Lakshya Raj Mar 27 '21 at 19:34