This question already has answers here:
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 1 year ago.
What’s the best/standard way of merging two associative arrays in JavaScript? Does everyone just do it by rolling their own for loop?
with jquery you can call $.extend
var obj1 = {a: 1, b: 2};
var obj2 = {a: 4, c: 110};
var obj3 = $.extend(obj1, obj2);
obj1 == obj3 == {a: 4, b: 2, c: 110} // Pseudo JS
(assoc. arrays are objects in js)
look here: http://api.jquery.com/jQuery.extend/
edit: Like rymo suggested, it's better to do it this way:
obj3 = $.extend({}, obj1, obj2);
obj3 == {a: 4, b: 2, c: 110}
As here obj1 (and obj2) remain unchanged.
edit2: In 2018 the way to do it is via Object.assign:
var obj3 = Object.assign({}, obj1, obj2);
obj3 === {a: 4, b: 2, c: 110} // Pseudo JS
If working with ES6 this can be achieved with the Spread Operator:
const obj3 = { ...obj1, ...obj2 };
Now in 2016 I would say the best/standard way is Object.assign()
Pure Javascript. No jQuery is needed.
obj1 = {a: 1, b: 2};
obj2 = {a: 4, c: 110};
obj3 = Object.assign({},obj1, obj2); // Object {a: 4, b: 2, c: 110}
More information, examples and polyfill here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
This is how Prototype does it:
Object.extend = function(destination, source) {
for (var property in source) {
if (source.hasOwnProperty(property)) {
destination[property] = source[property];
}
}
return destination;
};
called as, for example:
var arr1 = { robert: "bobby", john: "jack" };
var arr2 = { elizabeth: "liz", jennifer: "jen" };
var shortnames = Object.extend(arr1,arr2);
EDIT: added hasOwnProperty() check as correctly pointed out by bucabay in comments
Keep it simple...
function mergeArray(array1,array2) {
for(item in array1) {
array2[item] = array1[item];
}
return array2;
}
Underscore also has an extend method:
Copy all of the properties in the source objects over to the
destination object. It's in-order, so the last source will override
properties of the same name in previous arguments.
_.extend(destination, *sources)
_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
In dojo, the 2-objects/arrays "merge" would be lang.mixin(destination, source) -- you can also mix multiple sources into one destination, etc -- see the mixin function's reference for details.
do you want to overwrite a property if the names are the same but the values are not?
And do you want to permanently change one of the original objects,
or do you want a new merged object returned?
function mergedObject(obj1, obj2, force){
for(var p in obj1) this[p]= obj1[p];
for(var p in obj2){
if(obj2.hasOwnProperty(p)){
if(force || this[p]=== undefined) this[p]= obj2[p];
else{
n= 2;
while(this[p+n]!== undefined)++n;
this[p+n]= obj2[p];
}
}
}
}
Rolling Your Own Extend/Mixin Function
function extend(objects) {
var args
, first = Array.prototype.slice.call(arguments, 0, 1)[0]
, second;
if (arguments.length > 1) {
second = Array.prototype.splice.call(arguments, 1, 1)[0];
for (var key in second) {
first[key] = second[key];
}
args = Array.prototype.slice.call(arguments, 0);
return extend.apply(this, args);
}
return first;
}
...
var briansDirections = {
step1: 'Remove pastry from wrapper.',
step2: 'Place pastry toaster.',
step3: 'Remove pastry from toaster and enjoy.',
};
extend(briansDirections, { step1: 'Toast Poptarts' }, { step2: 'Go ahead, toast \'em' }, { step3: 'Hey, are you sill reading this???' });
...
This simply extends a splat of objects, recursively. Also, note that this recursive function is TCO (Tail-Call Optimized) as its return is the last call to itself.
Additionally, you may want targeted properties. In this case, you may want to condense objects based upon id, quantity, or another property. This approach could have a small book written about it and requires object-juxtaposition and can get very complex. I've written a small library for this which is available upon request.
Hope this helps!
In Javascript there is no notion of
associative array, there are objects
The only way to merge two objects is
to loop for their properties and
copy pointers to their values that
are not primitive types and values
for primitive types to another
instance
In 2019 you have 2 good options:
Object assigning [doc]
const result = Object.assign({}, baseObject, updatingObject);
Object spreading [doc]
const result = { ...baseObject, ...updatingObject};
The first one tends to be safer, more standard and polyvalent. A good pros and cons here
Yahoo UI (YUI) also has a helper function for this:
http://developer.yahoo.com/yui/examples/yahoo/yahoo_merge.html
YAHOO.namespace('example');
YAHOO.example.set1 = { foo : "foo" };
YAHOO.example.set2 = { foo : "BAR", bar : "bar" };
YAHOO.example.set3 = { foo : "FOO", baz : "BAZ" };
var Ye = YAHOO.example;
var merged = YAHOO.lang.merge(Ye.set1, Ye.set2, Ye.set3);
jquery has a boolean for deep copy. You could do something like that:
MergeRecursive = function(arr1, arr2){
$.extend(true, arr1, arr2);
return arr1;
};
Also you can edit this function to support n-arrays to merge.
ArrayMergeRecursive = function(){
if(arguments.length < 2){
throw new Error("ArrayMergeRecursive: Please enter two or more objects to merge!");
}
var arr1=arguments[0];
for(var i=0; i<=arguments.length; i++ ){
$.extend(true, arr1, arguments[i]);
}
return arr1;
};
So now you can do
var arr1 = {'color': {'mycolor': 'red'}, 3: 5},
arr2 = {4: 10, 'color': {'favorite': 'green', 0: 'blue'}},
arr3 = ['Peter','Jhon','Demosthenes'],
results = ArrayMergeRecursive(arr1, arr2, arr3); // (arr1, arr2 ... arrN)
console.log("Result is:", results);
I needed a deep-object-merging. So all of the other answers didn't help me very much. _.extend and jQuery.extend do well, unless you have a recursive array like i do. But it ain't so bad, you can program it in five minutes:
var deep_merge = function (arr1, arr2) {
jQuery.each(arr2, function (index, element) {
if (typeof arr1[index] === "object" && typeof element === "object") {
arr1[index] = deep_merge(arr1[index], element);
} else if (typeof arr1[index] === "array" && typeof element === "array") {
arr1[index] = arr1[index].concat(element);
} else {
arr1[index] = element;
}
});
return arr1;
}
To merge arrays in jQuery what about $.merge?
var merged = $.merge([{id:3, value:'foo3'}], [{id:1, value:'foo1'}, {id:2, value:'foo2'}]);
merged[0].id == 3;
merged[0].value == 'foo3';
merged[1].id == 1;
merged[1].value == 'foo1';
merged[2].id == 2;
merged[2].value == 'foo2';
Recursive solution (extends also arrays of objects) + null checked
var addProps = function (original, props) {
if(!props) {
return original;
}
if (Array.isArray(original)) {
original.map(function (e) {
return addProps(e, props)
});
return original;
}
if (!original) {
original = {};
}
for (var property in props) {
if (props.hasOwnProperty(property)) {
original[property] = props[property];
}
}
return original;
};
Tests
console.log(addProps([{a: 2}, {z: 'ciao'}], {timestamp: 13}));
console.log(addProps({single: true}, {timestamp: 13}));
console.log(addProps({}, {timestamp: 13}));
console.log(addProps(null, {timestamp: 13}));
[ { a: 2, timestamp: 13 }, { z: 'ciao', timestamp: 13 } ]
{ single: true, timestamp: 13 }
{ timestamp: 13 }
{ timestamp: 13 }
Here is the best solution.
obj1.unshift.apply( obj1, obj2 );
Also, obj1 can grow inside a loop without any problem. (lets say obj2 is dynamic)
Related
This sounds like a simple task, but I can't quite figure it out: I have an array :
var array = ['opt1','sub1','subsub1','subsubsub1']
From that I want to generate the following objects:
{
opt1:{
sub1:{
subsub1:{
subsubsub1:{}
}
}
}
}
I have a way to do it, making a string and using eval, but I'm looking to avoid that, any idea?
You could use reduce:
var array = ['opt1','sub1','subsub1','subsubsub1'];
var object = {};
array.reduce(function(o, s) { return o[s] = {}; }, object);
console.log(object);
But this was only introduced in ECMAScript 5.1, so it won't be supported in some older browsers. If you want something that will be supported by legacy browsers, you could use the polyfill technique described in the MDN article above, or a simple for-loop, like this:
var array = ['opt1','sub1','subsub1','subsubsub1'];
var object = {}, o = object;
for(var i = 0; i < array.length; i++) {
o = o[array[i]] = {};
}
console.log(object);
You can use reduceRight to transform the array into a 'chain' of objects:
const array = ['a', 'b', 'c'];
const object = array.reduceRight((obj, next) => ({[next]: obj}), {});
// Example:
console.log(object); // {"a":{"b":{"c":{}}}}
you could use lodash set function
_.set(yourObject, 'a.b.c')
You can use the following Function
function arr2nestedObject(myArray){
var cp_myArray = myArray;
var lastobj = {};
while(cp_myArray.length>0){
newobj = {};
var prop = cp_myArray.pop();
newobj[prop] = lastobj;
lastobj = newobj;
}
return lastobj;
}
The following code:
var myArray = ["personal-information", "address", "street",'Great-Success'];
console.log(JSON.stringify(arr2nestedObject(myArray),undefined,2));
Would Produce the Following Output:
{
"personal-information": {
"address": {
"street": {
"Great-Success": {}
}
}
}
}
Please let me know if that was what you meant.
Kind Regards.
As #p.s.w.g answer is a very good answer with pure js, but if you want an alternative with in a descriptive and functional way of that and set a value for final nested prop, you can use ramdajs assocPath https://ramdajs.com/docs/#assocPath like below:
var array = ['opt1','sub1','subsub1','subsubsub1'];
R.assocPath(array, "the value", {});
more details:
Makes a shallow clone of an object, setting or overriding the nodes
required to create the given path, and placing the specific value at
the tail end of that path. Note that this copies and flattens
prototype properties onto the new object as well. All non-primitive
properties are copied by reference.
examples:
R.assocPath(['a', 'b', 'c'], 42, {a: {b: {c: 0}}}); //=> {a: {b: {c: 42}}}
// Any missing or non-object keys in path will be overridden
R.assocPath(['a', 'b', 'c'], 42, {a: 5}); //=> {a: {b: {c: 42}}}
I am using _.isEqual that compares 2 array of objects (ex:10 properties each object), and it is working fine.
Now there are 2 properties (creation and deletion) that i need not to be a part of comparison.
Example:
var obj1 = {name: "James", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
var obj2 = {name: "Maria", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
// lodash method...
_.isEqual(firstArray, secondArray)
You can use omit() to remove specific properties in an object.
var result = _.isEqual(
_.omit(obj1, ['creation', 'deletion']),
_.omit(obj2, ['creation', 'deletion'])
);
var obj1 = {
name: "James",
age: 17,
creation: "13-02-2016",
deletion: "13-04-2016"
};
var obj2 = {
name: "Maria",
age: 17,
creation: "13-02-2016",
deletion: "13-04-2016"
};
var result = _.isEqual(
_.omit(obj1, ['creation', 'deletion']),
_.omit(obj2, ['creation', 'deletion'])
);
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
#ryeballar's answer is not great for large objects because you are creating a deep copy of each object every time you do the comparison.
It's better to use isEqualWith. For example, to ignore differences in the "creation" and "deletion" properties:
var result = _.isEqualWith(obj1, obj2, (value1, value2, key) => {
return key === "creation" || key === "deletion" ? true : undefined;
});
EDIT (important caveat pointed out in the comments): if objects have different numbers of keys, then isEqualWith considers them to be different, regadless of what your customizer does. Therefore do not use this approach if you want to ignore an optional property. Instead, consider using _.isMatch(), _.isMatchWith(), or #ryeballar's _.omit() approach.
Note that if you're writing for ES5 and earlier, you'll have to replace the arrow syntax (() => {) with function syntax (function() {)
_.omit creates deep copy of the object. If you need to exclude only root props it is better to create shallow copy using, for example, destructuring assignment:
const x = { a: 4, b: [1, 2], c: 'foo' }
const y = { a: 4, b: [1, 2], c: 'bar' }
const { c: xC, ...xWithoutC } = x
const { c: yC, ...yWithoutC } = y
_.isEqual(xWithoutC, yWithoutC) // true
xWithoutC.b === x.b // true, would be false if you use _.omit
Best way is not to create copies at all (TypeScript):
function deepEqual(
x?: object | null,
y?: object | null,
ignoreRootProps?: Set<string>
) {
if (x == null || y == null) return x === y
const keys = Object.keys(x)
if (!_.isEqual(keys, Object.keys(y)) return false
for (let key of keys) {
if (ignoreRootProps && ignoreRootProps.has(key)) continue
if (!_.isEqual(x[key], y[key])) return false
}
return true
}
You could map your array into a "cleaned" array, then compare those.
// Create a function, to do some cleaning of the objects.
var clean = function(obj) {
return {name: obj.name, age: obj.age};
};
// Create two new arrays, which are mapped, 'cleaned' copies of the original arrays.
var array1 = firstArray.map(clean);
var array2 = secondArray.map(clean);
// Compare the new arrays.
_.isEqual(array1, array2);
This has the downside that the clean function will need to be updated if the objects are expecting any new properties. It is possible to edit it so that it removes the two unwanted properties instead.
I see two options.
1) Make a second copy of each object that doesn't contain the creation or date.
2) Loop through all the properties and, assuming you know for certain that they both have the same properties, try something like this.
var x ={}
var y ={}
for (var property in x) {
if(property!="creation" || property!="deletion"){
if (x.hasOwnProperty(property)) {
compare(x[property], y[property])
}
}
}
Where compare() is some simple string or object comparison. If you are certain of the properties on one or both the objects, you can simplify this code a bit further, but this should work in most cases.
My final solution required a full comparison ignoring an optional property so the above solutions did not work.
I used a shallow clone to remove the keys I wanted to ignore from each object before comparing with isEqual:
const equalIgnoring = (newItems, originalItems) => newItems.length === originalItems.length
&& newItems.every((newItem, index) => {
const rest1 = { ...newItem };
delete rest1.creation;
delete rest1.deletion;
const rest2 = { ...originalItems[index] };
delete rest2.creation;
delete rest2.deletion;
return isEqual(rest1, rest2);
});
If you want to check a subset for each item in the array this works:
const equalIgnoringExtraKeys = (fullObjs, partialObjs) =>
fullObjs.length === partialObjs.length
&& fullObjs.every((fullObj, index) => isMatch(fullObj, partialObjs[index]));
If you also want to ignore a specific property and check subset:
const subsetIgnoringKeys = (fullObjs, partialObjs) =>
fullObjs.length === partialObjs.length
&& fullObjs.every((fullObj, index) => isMatchWith(
fullObj,
partialObjs[index],
(objValue, srcValue, key, object, source) => {
if (["creation", "deletion"].includes(key)) {
return true;
}
return undefined;
}
));
I need to create a new object based on the original with all the fields, but without some of them. In my current implementation I enumerate all the fields, and exclude don't needed:
var obj1 = {
a: 1,
b: 2,
c: 3
};
var keys = _.keys(obj);
_.remove(keys, 'c');
var obj2 = _.pick(obj1, keys);
console.log(obj2); // => { a: 1, b: 2 }
But I don't like that, can somebody suggest more easiest way? For example in mongoose, find method accepts a string of fields, separated by comma, and to exclude any field, I need to add minus symbol only: name email -password.
If you are using underscorejs it is pretty simple, just use ._omit()
var obj1 = {
a: 1,
b: 2,
c: 3
};
var obj2 = _.omit(obj1, ["c"]);
jsfiddle: https://jsfiddle.net/bpursley/hnh5ecam/
You could try something like this:
var obj2 = {},
forbiddenProperties = ['c', 'd'];
Object.keys(obj1).forEach(function (key) {
if (forbiddenProperties.indexOf(key) >= 0) {
obj2[key] = obj1[key];
}
});
You could create a function that does this, and you don't need to rely on underscore.
function partialClone(obj, excluded) {
var clone = {};
for (var key in obj) {
if (!excluded.includes(key)) {
clone[key] = obj[key];
}
}
return clone;
}
Working jsFiddle
This question already has answers here:
How to deeply merge two object values by keys
(5 answers)
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 6 years ago.
I have two json objects obj1 and obj2, i want to merge them and crete a single json object.
The resultant json should have all the values from obj2 and the values from obj1 which is not present in obj2.
Question:
var obj1 = {
"name":"manu",
"age":23,
"occupation":"SE"
}
var obj2 = {
"name":"manu",
"age":23,
"country":"india"
}
Expected:
var result = {
"name":"manu",
"age":23,
"occupation":"SE",
"country":"india"
}
There are couple of different solutions to achieve this:
1 - Native javascript for-in loop:
const result = {};
let key;
for (key in obj1) {
if(obj1.hasOwnProperty(key)){
result[key] = obj1[key];
}
}
for (key in obj2) {
if(obj2.hasOwnProperty(key)){
result[key] = obj2[key];
}
}
2 - Object.keys():
const result = {};
Object.keys(obj1)
.forEach(key => result[key] = obj1[key]);
Object.keys(obj2)
.forEach(key => result[key] = obj2[key]);
3 - Object.assign():
(Browser compatibility: Chrome: 45, Firefox (Gecko): 34, Internet Explorer: No support, Edge: (Yes), Opera: 32, Safari: 9)
const result = Object.assign({}, obj1, obj2);
4 - Spread Operator:
Standardised from ECMAScript 2015 (6th Edition, ECMA-262):
Defined in several sections of the specification: Array Initializer, Argument Lists
Using this new syntax you could join/merge different objects into one object like this:
const result = {
...obj1,
...obj2,
};
5 - jQuery.extend(target, obj1, obj2):
Merge the contents of two or more objects together into the first object.
const target = {};
$.extend(target, obj1, obj2);
6 - jQuery.extend(true, target, obj1, obj2):
Run a deep merge of the contents of two or more objects together into the target. Passing false for the first argument is not supported.
const target = {};
$.extend(true, target, obj1, obj2);
7 - Lodash _.assignIn(object, [sources]): also named as _.extend:
const result = {};
_.assignIn(result, obj1, obj2);
8 - Lodash _.merge(object, [sources]):
const result = _.merge(obj1, obj2);
There are a couple of important differences between lodash's merge function and Object.assign:
1- Although they both receive any number of objects but lodash's merge apply a deep merge of those objects but Object.assign only merges the first level. For instance:
_.isEqual(_.merge({
x: {
y: { key1: 'value1' },
},
}, {
x: {
y: { key2: 'value2' },
},
}), {
x: {
y: {
key1: 'value1',
key2: 'value2',
},
},
}); // true
BUT:
const result = Object.assign({
x: {
y: { key1: 'value1' },
},
}, {
x: {
y: { key2: 'value2' },
},
});
_.isEqual(result, {
x: {
y: {
key1: 'value1',
key2: 'value2',
},
},
}); // false
// AND
_.isEqual(result, {
x: {
y: {
key2: 'value2',
},
},
}); // true
2- Another difference has to do with how Object.assign and _.merge interpret the undefined value:
_.isEqual(_.merge({x: 1}, {x: undefined}), { x: 1 }) // false
BUT:
_.isEqual(Object.assign({x: 1}, {x: undefined}), { x: undefined })// true
Update 1:
When using for in loop in JavaScript, we should be aware of our environment specially the possible prototype changes in the JavaScript types. For instance some of the older JavaScript libraries add new stuff to Array.prototype or even Object.prototype.
To safeguard your iterations over from the added stuff we could use object.hasOwnProperty(key) to mke sure the key is actually part of the object you are iterating over.
Update 2:
I updated my answer and added the solution number 4, which is a new JavaScript feature but not completely standardized yet. I am using it with Babeljs which is a compiler for writing next generation JavaScript.
Update 3:
I added the difference between Object.assign and _.merge.
WORKING FIDDLE
Simplest Way with Jquery -
var finalObj = $.extend(obj1, obj2);
Without Jquery -
var finalobj={};
for(var _obj in obj1) finalobj[_obj ]=obj1[_obj];
for(var _obj in obj2) finalobj[_obj ]=obj2[_obj];
1)
var merged = {};
for(key in obj1)
merged[key] = obj1[key];
for(key in obj2)
merged[key] = obj2[key];
2)
var merged = {};
Object.keys(obj1).forEach(k => merged[k] = obj1[k]);
Object.keys(obj2).forEach(k => merged[k] = obj2[k]);
OR
Object.keys(obj1)
.concat(Object.keys(obj2))
.forEach(k => merged[k] = k in obj2 ? obj2[k] : obj1[k]);
3) Simplest way:
var merged = {};
Object.assign(merged, obj1, obj2);
Just another solution using underscore.js:
_.extend({}, obj1, obj2);
I've used this function to merge objects in the past, I use it to add or update existing properties on obj1 with values from obj2:
var _mergeRecursive = function(obj1, obj2) {
//iterate over all the properties in the object which is being consumed
for (var p in obj2) {
// Property in destination object set; update its value.
if ( obj2.hasOwnProperty(p) && typeof obj1[p] !== "undefined" ) {
_mergeRecursive(obj1[p], obj2[p]);
} else {
//We don't have that level in the heirarchy so add it
obj1[p] = obj2[p];
}
}
}
It will handle multiple levels of hierarchy as well as single level objects. I used it as part of a utility library for manipulating JSON objects. You can find it here.
This simple function recursively merges JSON objects, please notice that this function merges all JSON into first param (target), if you need new object modify this code.
var mergeJSON = function (target, add) {
function isObject(obj) {
if (typeof obj == "object") {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
return true; // search for first object prop
}
}
}
return false;
}
for (var key in add) {
if (add.hasOwnProperty(key)) {
if (target[key] && isObject(target[key]) && isObject(add[key])) {
this.mergeJSON(target[key], add[key]);
} else {
target[key] = add[key];
}
}
}
return target;
};
BTW instead of isObject() function may be used condition like this:
JSON.stringify(add[key])[0] == "{"
but this is not good solution, because it's will take a lot of resources if we have large JSON objects.
I have a JSON webservice in the following format.
{ Name:['a','b'], Name:['cd','ef'], Age:{...}, Address:{...} }.
Here I have 2 arrays & 2 objects inside an object and these (array & objects) numbers may vary. What I need is, how can I get the number of Arrays alone from the main Object? There may exist another way to solve my problem but I need my code to be in a .JS (javascript file).
When I tried:
Object.keys(mainobject).length;
It gives total count of array + objects in main object.
var data = { Name:['a','b'], OtherName:['cd','ef'], Age:{a: 12}, Address:{a: 'asdf'} }
var numberOfArrays = Object.keys(data).filter(function(key) {
return data[key] instanceof Array; //or Array.isArray(data[key]) if the array was created in another frame
}).length;
alert(numberOfArrays);
Note: This won't work in older versions of IE
jsFiddle
To make it work with browsers that don't support it, use the shims from MDN:
Object.keys
Array.filter
I'd write something to count all the types
var obj = {
k1: ['a','b'], k2: ['cd','ef'],
k3: 0, k4: 1,
k5: {a:'b'},
k6: new Date(),
k7: "foo"
};
function getType(obj) {
var type = Object.prototype.toString.call(obj).slice(8, -1);
if (type === 'Object') return obj.constructor.name;
return type;
}
function countTypes(obj) {
var k, hop = Object.prototype.hasOwnProperty,
ret = {}, type;
for (k in obj) if (hop.call(obj, k)) {
type = getType(obj[k]);
if (!ret[type]) ret[type] = 1;
else ++ret[type];
}
return ret;
}
var theTypes = countTypes(obj);
// Object {Array: 2, Number: 2, Object: 1, Date: 1, String: 1}
Now if I wanted to know the number of Arrays only
var numArrays = theTypes.Array || 0; // 2 Arrays in this example
You could try checking the type of each member of your object.
var count = 0;
for (var foo in mainobject) {
if (foo instanceof Array) count++;
}
Now all you have to do is read the value of count.