Lodash - difference between .extend() / .assign() and .merge()
Here's how extend
/assign
works: For each property in source, copy its value as-is to destination. if property values themselves are objects, there is no recursive traversal of their properties. Entire object would be taken from source and set in to destination.
Here's how merge
works: For each property in source, check if that property is object itself. If it is then go down recursively and try to map child object properties from source to destination. So essentially we merge object hierarchy from source to destination. While for extend
/assign
, it's simple one level copy of properties from source to destination.
Here's simple JSBin that would make this crystal clear:
http://jsbin.com/uXaqIMa/2/edit?js,console
Here's more elaborate version that includes array in the example as well:
http://jsbin.com/uXaqIMa/1/edit?js,console
Which is better to extend a Javascript Object with lodash? Whats the difference?
Adding properties to an object's prototype property is pretty much useless unless that object happens to be a function that is going to be used as a constructor. If it's any object that is not also a function, prototype
is just an ordinary property:
var obj = {};
try {
obj.prototype.someProp = 5; // Throws error, obj.prototype is undefined
} catch ( e ) {
obj.prototype = {}; // Nothing special about this
obj.prototype.someProp = 5; // or this
obj.someProp === undefined; // true
}
Adding properties to the actual prototype of an object (not the prototype property) affects all objects with that same prototype:
var obj = {};
Object.getPrototypeOf( obj ).someProp = 5;
var obj2 = {};
obj2.someProp === 5; // true
That doesn't seem to be what you want.
_.assign and Object.assign extend the actual object:
var obj = {};
Object.assign( obj, { someProp: 5 } );
obj.someProp === 5; // true
How to extend and merge a list of objects with another list using lodash
The easy way is to use the _.flatten
method of lodash to achieve this:
const _ = require('lodash');
const data = _.map(api_response.environment_list, e =>
_.map(api_response.member_list, m => ({
id: m.id,
name: m.name,
role: m.role,
environment: e
})
); // This step achieves the exact same thing that you have achieved up until now in your question.
const flattenedData = _.flatten(data); // This is the method you need to make the array "flat".
console.log(flattenedData);
You could also attempt to do this without using lodash in this, a bit more efficient, way:
const output = [];
for (const e of api_response.environment_list) {
for (const m of api_response.member_list) {
const obj = {
id: m.id,
name: m.name,
role: m.role,
environment: e
};
output.push(obj);
}
}
Object.assign vs $.extend
The two key differences are the optional boolean for deep
merge which is recursive on the jQuery $.extend
method (where false
is not supported?!) ...
let object1 = {
id: 1,
name: {
forename: 'John',
surname: 'McClane'
},
};
let object2 = {
id: 2,
name: {
}
};
// merge objects
let objExtend = $.extend(true, {}, object1, object2);
let objAssign = Object.assign({}, object1, object2);
// diff
console.log(objExtend.name.forename); // "John"
console.log(objAssign.name.forename); // undefined
Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.
Example: JsFiddle
The second is the $.extend
method ignores undefined
...
let object1 = {
id: 1,
name: 'hello world'
};
let object2 = {
id: 2,
name: undefined
};
// merge objects
let objExtend = $.extend({}, object1, object2);
let objAssign = Object.assign({}, object1, object2);
// diff
console.log(objExtend.name); // "hello world"
console.log(objAssign.name); // undefined
Example: JsFiddle
Docs
MDN: Object.assign(target, ...sources)
jQuery: jQuery.extend([deep], target, object1 [, objectN])
Additionally:
If you are looking for a way to deep merge objects without jQuery, this answer is excellent:
How to deep merge instead of shallow merge?
Example: JsFiddle
How to deep merge using Object.assign
with ES6:
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
function mergeDeep(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
Lo-dash extend/assign default behavior
This has been answered in the comments already, but here's an attempt on a more qualified answer and to close this topic:
The effect you're getting is due to combined effect of node.js module loading and lodash _.extend():
- Included modules are just objects and they are cached, so every subsequent call to 'require()' relating to the same physical file system path will return that object
- Lodash's extend/assign is annoyingly destructive, it's manipulates the passed object directly instead of returning a new object (like e.g. Rubys 'Hash#merge()' would do)
So you have one object representing all.js
and _.assign()
manipulates that. Any further require()
's will just return the same, modified object.
If this is undesired (and in many situations it is), only workaround I know of so far is this:
var someOptions = {/*...*/};
var extended = _.extend(_.clone(someOptions))
Related Topics
JavaScript Function Order: Why Does It Matter
Getting Key with the Highest Value from Object
Remove HTML Tags in JavaScript with Regex
Jquery.Getjson - Access-Control-Allow-Origin Issue
Random Alpha-Numeric String in JavaScript
Is Reading the 'Length' Property of an Array Really That Expensive an Operation in JavaScript
How to Shuffle the Characters in a String in JavaScript
JavaScript Regular Expressions and Sub-Matches
Date.Setmonth' Causes the Month to Be Set Too High If 'Date' Is at the End of the Month
Split String Only on First Instance of Specified Character
Regex for Match/Replacing JavaScript Comments (Both Multiline and Inline)
Handle Url Fragment Identifier (Anchor) Change Event in JavaScript
How to Pass Parameters to a Script Tag
Validate That a String Is a Positive Integer
Plain Count Up Timer in JavaScript