2

I have a bunch of JSON with multiple objects, arrays, strings, booleans, numbers etc. which are stored in one object at the root level and component.

Here is a sample:

{
  "theme": {
    "auto": {
      "sensor": "sensor.sn1_ldr",
      "below": 600
    },
    "ui": {
      "cards": {
        "round": false,
        "elevation": 1
      }
    },
    ...
  },
  ...
}

I have managed to pass back the path and new value of the item in an array like so:

["theme", "auto", "sensor"]

How do I from there set the new value of that path? ie. the equivalent of:

config.theme.auto.sensor = newValue;

but using the path array passed back?

Method I have so far:

handleConfigChange = (path, value) => {
  console.log(path, value);
  let config = this.state.config;
  // Set the new value

  this.setState({ config });
};
Timmo
  • 2,196
  • 4
  • 31
  • 53
  • 1
    At least very very related: https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key The function in the accepted answer starts out by converting a given string into an array just like yours. – T.J. Crowder Sep 22 '18 at 15:42
  • 1
    you should defiantly read [this](https://medium.com/javascript-inside/safely-accessing-deeply-nested-values-in-javascript-99bf72a0855a) – Sagiv b.g Sep 22 '18 at 15:45
  • is the number of elements in array is always same ? – aravind_reddy Sep 22 '18 at 15:49

3 Answers3

3

You could store the last key and reduce the object by taking the keys from the path.

function setValue(object, path, value) {
    var last = path.pop();
    path.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
}

var config = { theme: { auto: { sensor: "sensor.sn1_ldr", below: 600 }, ui: { cards: { round: false, elevation: 1 } } } },
    path = ["theme", "auto", "sensor"];

setValue(config, path, 'foo');

console.log(config);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 351,820
  • 24
  • 303
  • 358
  • Thanks for that! I'll have to do a bit of research into how on earth it works but it solves my problem – Timmo Sep 22 '18 at 15:55
2

Below is a simple version of Nina's answer (the one using reduce).

Hope this helps you get what's going on behind that.

const setValueByPathArray = (object, path, value) => {
  let i = 0
  let reference = object
  while (i < path.length - 1) {
    const currentPath = path[i]
    reference = reference[currentPath]
    i += 1
  }
  const lastPath = path[path.length - 1]
  reference[lastPath] = value
  return object
}

const config = { theme: { auto: { sensor: "sensor.sn1_ldr", below: 600 }, ui: { cards: { round: false, elevation: 1 } } } }
const path = ["theme", "auto", "sensor"];

setValueByPathArray(config, path, 'foo')
Muhammet Enginar
  • 478
  • 6
  • 13
0
  1. Use Object.key to get the key Array:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

  1. Use recursion to loop through.

    const object1 = {
      "theme": {
        "auto": {
          "sensor": "sensor.sn1_ldr",
          "below": 600
        },
        "ui": {
          "cards": {
            "round": false,
            "elevation": 1
          }
        },
    
      },  
    }
    
    var result = [];
    see(object1);
    console.log(result);
    
    function see (obj){
        var k = Object.keys(obj)
        for (var i = 0; i < k.length; i++){
            result.push(k[i]);
            if (obj[k[i]] instanceof Object){
                see(obj[k[i]]);
            }
        }
    }
    
S.S. Anne
  • 14,415
  • 7
  • 35
  • 68
Terry D
  • 309
  • 1
  • 14