0

Let's say I have a dictionary like this (this is just example data):

obj = {
    name: 'John',
    properties: {
       age: 42,
       address: 'somewhere'
    }
}

What if I wanted to modify a value of any depth, specified by a variable?

For depth 1 it would be direct:

obj[what_to_modify] = new_value

but for depth > 1?

what_to_modify="properties.age"
obj[what_to_modify.split('.')] = new_value
// I KNOW, THIS DOESN'T WORK, IT JUST
// ILLUSTRATES WHAT I WANT TO ACHIEVE

My solution was to do a foor lop like this:

let levels = what_to_modify.split('.')
let it = obj
for(let k=0; k < levels.length; k++){
    let key = levels[k]
    if(k == levels.length -1){
        it[key] = new_value
    }else{
        it = it[key]
    }
}

This already works, but I wanted to know: Is there a less messy way to do it?


To be clear, I don't want to just read the value, I want to modify it, so Accessing nested JavaScript objects and arrays by string path and Access property via it's keyPath in Javascript? don't solve my problem


IMPORTANT

The accepted answer uses lodash, but actually I shouldn't include new dependencies in my project, so if there's any other way with vanilla JS, I will change the accepted answer to that.

Miguel
  • 2,130
  • 1
  • 11
  • 26
  • Does this answer your question? [Access property via it's keyPath in Javascript?](https://stackoverflow.com/questions/17683616/access-property-via-its-keypath-in-javascript) – Damiaan Dufaux May 14 '21 at 17:50
  • Does this answer your question? [Accessing nested JavaScript objects and arrays by string path](https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-and-arrays-by-string-path) – mdeamp May 14 '21 at 17:50
  • @DamiaanDufaux It would if it allowed me to modify it, not only to read it – Miguel May 14 '21 at 17:56
  • @mdeamf same as with Damian, but also it's as messy as my proposed solution – Miguel May 14 '21 at 17:57

2 Answers2

2

You can use lodash. _.set sets a value by path and _.get returns a value by path. _.toPath converts a string to a path array.

const obj = {
    name: 'John',
    properties: {
       age: 42,
       address: 'somewhere'
    }
};

const propertiesAgePath = _.toPath('properties.age');
console.log(_.get(obj, propertiesAgePath));
_.set(obj, propertiesAgePath, 43);
console.log(_.get(obj, propertiesAgePath));
_.set(obj, _.toPath('properties.address'), 'new address');
console.log(obj);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
  • I'm going to upvote and accept, but actually I shouldn't include new dependencies to the project I'm working on, so if there's any other answer that can do that for me without lodash, I will have to change it. Thanks anyway – Miguel May 14 '21 at 18:07
  • @Miguel That's very sad that so many people insist in reinventing the wheel. That's a solved problem and lodash has so many helpful tools that I add it to most projects I'm working in. It's great for deep clones, creating ranges, complex object modifications, ... –  May 14 '21 at 18:10
1

You can also carefully use eval to accomplish this.

let new_value = 9

obj = {
    name: 'John',
    properties: {
       age: 42,
       address: 'somewhere'
    }
}

what_to_modify="properties.age"

eval("obj."+what_to_modify+"= new_value")

console.log(obj)
sychordCoder
  • 230
  • 3
  • 14
  • 1
    Please read [Never use eval()!](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!) –  May 14 '21 at 18:00
  • I'm not going to include eval in my code, but thank you for taking your time to answer. – Miguel May 14 '21 at 18:05
  • eval is dangerous depending on what you put in there. Understand your desire to not use it. – sychordCoder May 14 '21 at 18:09
  • It's not only dangerous but also slow. It can easily be replaced with functions with better performance. –  May 14 '21 at 18:13