Updating JavaScript object-attributes from another object
Solution 1
function update(obj/*, …*/) {
for (var i=1; i<arguments.length; i++) {
for (var prop in arguments[i]) {
var val = arguments[i][prop];
if (typeof val == "object") // this also applies to arrays or null!
update(obj[prop], val);
else
obj[prop] = val;
}
}
return obj;
}
should do the trick: update(currentObject, updateObject)
. You might want to add some type checks, like Object(obj) === obj
to extend only real objects with real objects, use a correct loop for arrays or hasOwnProperty
tests.
Solution 2
I suggest using underscore.js (or better, lo-dash) extend:
_.extend(destination, *sources)
Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.
_.extend({name: 'moe'}, {age: 50}); => {name: 'moe', age: 50}
Solution 3
Here's an Object.keys
and recursive example:
// execute object update function
update(currentObject, updateObject)
// instantiate object update function
function update (targetObject, obj) {
Object.keys(obj).forEach(function (key) {
// delete property if set to undefined or null
if ( undefined === obj[key] || null === obj[key] ) {
delete targetObject[key]
}
// property value is object, so recurse
else if (
'object' === typeof obj[key]
&& !Array.isArray(obj[key])
) {
// target property not object, overwrite with empty object
if (
!('object' === typeof targetObject[key]
&& !Array.isArray(targetObject[key]))
) {
targetObject[key] = {}
}
// recurse
update(targetObject[key], obj[key])
}
// set target property to update property
else {
targetObject[key] = obj[key]
}
})
}
JSFiddle demo (open console).
Solution 4
A simple implementation would look like this.
function copyInto(target /*, source1, sourcen */) {
if (!target || typeof target !== "object")
target = {};
if (arguments.length < 2)
return target;
for (var len = arguments.length - 1; len > 0; len--)
cloneObject(arguments[len-1], arguments[len]);
return target;
}
function cloneObject(target, source) {
if (!source || !target || typeof source !== "object" || typeof target !== "object")
throw new TypeError("Invalid argument");
for (var p in source)
if (source.hasOwnProperty(p))
if (source[p] && typeof source[p] === "object")
if (target[p] && typeof target[p] === "object")
cloneObject(target[p], source[p]);
else
target[p] = source[p];
else
target[p] = source[p];
}
This assumes no inherited properties should be cloned. It also does no checks for things like DOM objects, or boxed primitives.
We need to iterate in reverse through the arguments so that the copy is done in a right to left matter.
Then we make a separate cloneObject
function to handle the recursive copying of nested objects in a manner that doesn't interfere with the right to left copying of the original object arguments.
It also ensures that the initial target is a plain object.
The cloneObject
function will throw an error if a non-object was passed to it.
Luca Hofmann
Game developer, computer graphics, Android, Java, JavaScript etc.
Updated on July 09, 2022Comments
-
Luca Hofmann almost 2 years
I want to update an object that could look like this:
currentObject = { someValue : "value", myObject : { attribute1 : "foo", attribute2 : "bar" } };
.. with an object that contains some changes i.e.:
updateObject = { myObject : { attribute2 : "hello world" } };
At the end I would like to have currentObject updated so that:
currentObject.myObject.attribute2 == "hello world"
That should be posible for other objects as well.. As a solution I thought about iterating over the object and somehow take care of the namespace. But I wonder if there is an easy solution for that problem by using a library like jQuery or prototype.
-
gen_Eric almost 12 yearsIf you use jQuery, there is
$.extend
that should do what you want. -
Bergi almost 12 years@RocketHazmat: No, it is not recursive.
-
gen_Eric almost 12 years@Bergi: If you pass
true
as the 1st parameter, it is! ;-) -
Ian almost 12 years@Bergi Please look something up before you claim something about it. Here's a link so you can read about
.extend
: api.jquery.com/jQuery.extend -
Bergi almost 12 years@RocketHazmat: Right, I always forget that (I don't like the function much because of its array handling)
-
-
Bergi almost 12 yearsWhy does this copy from source to source to … to target?
-
Admin almost 12 years@Bergi: Just a different approach I guess, but I do think that I should make it non-destructive by creating interim objects instead of modifying the originals. Also, I should really differentiate between Arrays and Objects in case there's a
{foo:[...]} <- {foo:{...}}
situation. -
Bergi almost 12 yearsYes, it should be nondestructive for the sources - but you don't need interim objects, just
cloneInto(target)
. And yes, extending plain objects with arrays is a pain :-) -
TataBlack almost 9 yearsAs far as I can see, both Underscore's and Lodash's
extend
will overwrite themyObject
property, instead of simply updating it (i.e. it will contain onlyattribute2
). Lodash'smerge
would work, however. -
Season over 8 yearsAs pointed out by @TataBlack, given
var a = {name: 'moe', age: 50}, b = {age: 30}; var c = _.merge({}, a, b);
,c
will be{name: 'moe', age: 30}
whilea
andb
stay unchanged.