0

the code below is an object from database

{
  "id_": "1",
  "name": "Enemy x",
  "image": "image.png",
  "stats": {
    "level": 1,
    "attack": 5,
    "defense": 0
  }
}

And need edit attack or defense, but with a "pointer" saved in a constant, All this in a dynamic way since the value to edit could be in a third or fourth level of the object. for example:

const edit = 'stats/attack';
// const edit = 'stats/other/example'; <- example

I have some ideas but I don't know if exist best practices for this, or how to look for it on the internet since I can't find anything like it.

3 Answers3

1

use a reduce:

const data = 
  { id_   : '1'
  , name  : 'Enemy x'
  , image : 'image.png'
  , stats: 
    { level   : 1
    , attack  : 5
    , defense : 0
  } }

function getVal( obj, key )
  {
  return key.split('/').reduce((o,v)=>o[v],obj)
  }
  
  
console.log('--> ', getVal( data, 'stats/attack' ))

In case of a wrong path:

const data = 
  { id_   : '1'
  , name  : 'Enemy x'
  , image : 'image.png'
  , stats: 
    { level   : 1
    , attack  : 5
    , defense : 0
  } }

const getVal = ( obj, path ) =>
  path.split('/').reduce((o,v)=> o?.[v] ,obj) // better than `o[v] ?? 'error!`'
 
console.log('1-->', getVal( data, 'stats/attack' )) // --> 5    
console.log('2-->', getVal( data, 'stats/xxx' ))   //  --> undefined
console.log('3-->', getVal( data, 'zzz/xxx' ))    //   --> undefined
console.log('4-->', getVal( data, '' ))          //    --> undefined


//  thanks to pilchard comment
console.log('5-->', getVal( data, 'stats/xxx/1' )) //  --> undefined
.as-console-wrapper {max-height: 100% !important;top: 0;}
.as-console-row::after {display: none !important;}

PO : if I want to change it to 10. I will test to see if I can do it with this method

maybe that ?
usage 1 : get the value

let val = onElm( data, 'stats/attack' )  

usage 2 : change the value (to 10)

let val = onElm( data, 'stats/attack', 10 ) 

Demo;

const data = 
  { id_   : '1'
  , name  : 'Enemy x'
  , image : 'image.png'
  , stats: 
    { level   : 1
    , attack  : 5
    , defense : 0
  } }

const onElm = ( obj, path, newVal=null ) =>
  path.split('/').reduce((o,p,i,{length:n}) =>
    (++i<n) ? o[p] : newVal ? o[p] = newVal : o[p], obj)
 

console.log('get value of "stats/attack"\n  return value ->'
           , onElm( data, 'stats/attack'  ) ) // return value -> 5

console.log('change value of "stats/attack to 10"\n  return value ->'
           ,  onElm( data, 'stats/attack', 10) ) // return value ->  10

console.log ( '\nverify data Object now with new value: \n', data  )
.as-console-wrapper {max-height: 100% !important;top: 0;}
.as-console-row::after {display: none !important;}
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
  • Do have to be defensive with it could return undefined – Daniel A. White Jun 29 '22 at 21:31
  • Great, I need something like this, but to edit the "attack" field. for example if I want to change it to 10. I will test to see if I can do it with this method – Jonathanpadilla Jun 29 '22 at 22:18
  • your `wrong path` example has problems since it returns a string to the next iteration of the reduce. `stats/xxx/1` --> `r`. Better to just use optional chaining and return undefined as expected when a property isn't present `reduce((o,v) => o?.[v], obj)` – pilchard Jun 29 '22 at 23:11
  • Of course it's also an 11 year old duplicate – pilchard Jun 29 '22 at 23:15
  • @pilchard I didn't see my third answer in the "duplicate link"... ? – Mister Jojo Jun 29 '22 at 23:30
  • You mean to set the value? It's included in [this answer](https://stackoverflow.com/a/43849204/13762301) – pilchard Jun 30 '22 at 08:16
0

You can use lodash zipObjectDeep to achieve that but path works via '.', so you need to

const edit = 'stats/attack';
const path = edit.split('/').join('.')
zipObjectDeep([path], [1]);

Lodash zipObjectDeep doc

Yan Koshelev
  • 1,059
  • 1
  • 5
  • 10
-1

Replace all / with . then eval the string that represents obj.this.that.

Bonus: This works for arrays too "stats/level[2]/other"

var obj = {
  "id_": "1",
  "name": "Enemy x",
  "image": "image.png",
  "stats": {
    "level": 1,
    "attack": 5,
    "defense": 0
  }
};

var path = "stats/level";
var value = (eval ("obj." + path.replace(/\//g, ".")));
console.log(value)
IT goldman
  • 14,885
  • 2
  • 14
  • 28