rich Posted September 6, 2013 Share Posted September 6, 2013 This one is bugging me, but basically.. I want to have a function that takes a key and a new value and set that across multiple objects. I'm using this approach:child[key] = value;Which works great if you do:update('y', 100);Because the child object has a y property. However what I need to support is this too:update('body.velocity.y', 100);Now the 'body.velocity' object exists as a property of child, but of course using a string like this breaks the child[key] approach, as it's looking for a property actually named 'body.velocity.y'. Is there another way of doing this? (it has to be FAST, this is for an internal loop, so I don't want to have to be splitting strings and the likes if I can help it). Quote Link to comment Share on other sites More sharing options...
rich Posted September 6, 2013 Author Share Posted September 6, 2013 I've been toying with the idea of using the arguments property within the function. So you'd specify it as:update('body','velocity','y', 100);.. instead, but still need a way to convert the values of the arguments array into a check against the child object. Quote Link to comment Share on other sites More sharing options...
Ezelia Posted September 6, 2013 Share Posted September 6, 2013 the only other way I see here is by using eval() ... Quote Link to comment Share on other sites More sharing options...
rich Posted September 6, 2013 Author Share Posted September 6, 2013 Yeah, slow as hell though. It's ok, I've found a way of doing it I'm happy with that only involves one call to split. Quote Link to comment Share on other sites More sharing options...
Son of Bryce Posted September 6, 2013 Share Posted September 6, 2013 Which technique did you end up using? Quote Link to comment Share on other sites More sharing options...
ooflorent Posted September 6, 2013 Share Posted September 6, 2013 Yeah, slow as hell though. It's ok, I've found a way of doing it I'm happy with that only involves one call to split.I think that's the best option.Did you try using a while loop combined with indexOf() using an offset? In some languages it performs faster. Quote Link to comment Share on other sites More sharing options...
ooflorent Posted September 6, 2013 Share Posted September 6, 2013 Maybe you could add a third parameter called 'deep' to prevent the parsing of simple property string. Quote Link to comment Share on other sites More sharing options...
Qqwy Posted September 6, 2013 Share Posted September 6, 2013 I think this is a great opportunity for a recursive function:function update(object, key, value){ var nestedarr = String.split(key, "."); nestedUpdate(object, nestedarr, value);}function nestedUpdate(object, nestedarr, value){ if(nestedarr.length > 1){ nestedUpdate(object[nestedarr[0]], nestedarr.shift()) //Pop first element off array }else{ object[nestedarr[0]] = value; }}Note that this specific example is untested, the naming is horrible and might have bugs, but you get the general idea. Quote Link to comment Share on other sites More sharing options...
Chris Posted September 8, 2013 Share Posted September 8, 2013 Well, splitting the string on its dots and then Hangling through the objects should work and is imho the cleanest approach.About the performance, what about this:Create a hashtable that uses the whole string as key and stores references to the object it points to.So you dont have to split the string all the times and hangle trough all objects but can grab your target directly from the cache to read/write on it. p01 1 Quote Link to comment Share on other sites More sharing options...
p01 Posted September 8, 2013 Share Posted September 8, 2013 Chris: Good thinking about the memo-izing/caching. Quote Link to comment Share on other sites More sharing options...
xerver Posted September 8, 2013 Share Posted September 8, 2013 I've done this quite a bit in different places, this is what I normally use://the object to manipulatevar obj = { some: { deep: { prop: 1 } }}; //get the levels to walkvar levels = 'some.deep.prop'.split('.'), // leave the last element i = levels.length-1, //the object to use to walk o = obj;//walk to the level just before our propertywhile(i--){ o = o[levels.shift()];}//use the last key in the array to set the propertyo[levels.shift()] = 'foobar';Your case would be something like: MyObject.prototype.update = function(key, value) { //get the levels to walk var levels = key.split('.'), // leave the last element i = levels.length-1, //the object to use to walk o = this; //walk to the level just before our property while(i--){ o = o[levels.shift()]; } //use the last key in the array to set the property o[levels.shift()] = value;} Should work fine for cases like update('body.velocity.y', 10);andupdate('id', 10); Hope this helps! Quote Link to comment Share on other sites More sharing options...
ooflorent Posted September 9, 2013 Share Posted September 9, 2013 I think this one performs faster:MyObject.prototype.update = (function() { var cache = {}, props, i, n, o; return function(key, value, deep) { o = this; if (deep) { if (!(props = cache[key])) { cache[key] = props = key.split('.'); } for (i = 0, n = props.length - 1; i < n; i++) { o = o[props[i]]; } o[props[n]] = value; } else { o[key] = value; } };})(); xerver 1 Quote Link to comment Share on other sites More sharing options...
xerver Posted September 9, 2013 Share Posted September 9, 2013 Nice call! Caching definately adds some performance boosting (roughly x2) http://jsperf.com/deep-property-modification Even if you remove the deep check, you still get a big performance boost from caching the key splits. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.