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 });
};
3
  • 1
    At least very very related: stackoverflow.com/questions/6491463/… The function in the accepted answer starts out by converting a given string into an array just like yours. Commented Sep 22, 2018 at 15:42
  • 1
    you should defiantly read this Commented Sep 22, 2018 at 15:45
  • is the number of elements in array is always same ? Commented Sep 22, 2018 at 15:49

4 Answers 4

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

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

1 Comment

Thanks for that! I'll have to do a bit of research into how on earth it works but it solves my problem
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')

Comments

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

Comments

0

You can iterate over the path, keying into each level of the object until you're at the location you'd like to modify. I'm assuming valid path here, but you can raise an error if root becomes falsey (or a non-object) in the loop:

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

const path = ["theme", "auto", "sensor"];
const newValue = "Hello World!";
let root = tree;

for (let i = 0; i < path.length - 1; i++) {
  root = root[path[i]];
}

root[path.at(-1)] = newValue;

console.log(tree);

path.slice(0, -1).forEach also works if the traditional for loop makes you uneasy.

Comments

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.