Skip to content

Instantly share code, notes, and snippets.

@dysfunc
Last active March 2, 2017 01:30
Show Gist options
  • Save dysfunc/9701950 to your computer and use it in GitHub Desktop.
Save dysfunc/9701950 to your computer and use it in GitHub Desktop.
angular.extend - shallow + deep copy (supports merging of arrays and deduping)
/**
* Deep copy example:
* angular.extend(true, { hello: 'world', app: { id: '1234', groups: [{ id: 1},2,3,4,5] }, ids: [1,2,3] }, { app: { name: 'bond', groups: [6, 7, {hello:'world', test: [1,2,3,4, [12,34,45]]}, 9] }, ids: [4,5,6,3] });
* => "{"hello":"world","app":{"id":"1234","groups":[{"id":1},2,3,4,5,6,7,{"hello":"world","test":[1,2,3,4,[12,34,45]]},9],"name":"bond"},"ids":[1,2,3,4,5,6,3]}"
*
* Deep copy and dedup arrays
* angular.extend(true, true, { hello: 'world', app: { id: '1234', groups: [{ id: 1},2,3,4,5] }, ids: [1,2,3] }, { app: { name: 'bond', groups: [6, 7, {hello:'world', test: [1,2,3,4, [12,34,45]]}, 9] }, ids: [4,5,6,3] });
* => "{"hello":"world","app":{"id":"1234","groups":[{"id":1},2,3,4,5,6,7,{"hello":"world","test":[1,2,3,4,[12,34,45]]},9],"name":"bond"},"ids":[1,2,3,4,5,6]}"
*
* vs jQuery deep copy
* jQuery.extend(true, { hello: 'world', app: { id: '1234', groups: [{ id: 1},2,3,4,5] }, ids: [1,2,3] }, { app: { name: 'bond', groups: [6, 7, {hello:'world', test: [1,2,3,4, [12,34,45]]}, 9] }, ids: [4,5,6,3] });
* => {"hello":"world","app":{"id":"1234","groups":[6,7,{"hello":"world","test":[1,2,3,4,[12,34,45]]},9,5],"name":"bond"},"ids":[4,5,6]}"
*
* Shallow copy: (same as everyone else)
* angular.extend({ hello: [1,2,3,4] },{ hello: [5,6,7,8] })
* => "{"hello":[5,6,7,8]}"
*/
/*
* Determine whether an Object is a plain object or not (created using "{}" or "new Object")
* @param {Object} obj Object we want to check
* @return {Boolean} True/False result
*/
angular.isPlainObject = function(obj){
return !(typeof(obj) !== 'object' || obj && obj.nodeType || obj !== null && obj === obj.window || obj && obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf'));
};
/**
* Removes duplicates from an Array
* @param {Array} Array Array to dedup
* @return {Array} Array containing only unique values
*/
angular.unique = function(array){
var a = array.concat();
for(var i = 0; i < a.length; ++i){
for(var j = i + 1; j < a.length; ++j){
if(a[i] === a[j]){
a.splice(j--, 1);
}
}
}
return a;
};
/**
* Merge the contents of two or more objects into the target object
* @param {Boolean} deep If true, the merge becomes recursive (optional)
* @param {Object} target The object receiving the new properties
* @param {Object} arguments One or more additional objects to merge with the first
* @return {Object} The target object with the new contents
*
* angular.extend(object, object2) // shallow copy
* angular.extend(true, object, object2) // deep copy
* angular.extend(true, true, object, object2) // deep copy + dedup arrays
*/
angular.extend = function(target){
var i = 1,
deep = false,
dedup = false;
if(typeof(target) === 'boolean'){
deep = target;
target = arguments[1] || {};
i++;
if(typeof(target) === 'boolean'){
dedup = target;
target = arguments[2] || {};
i++;
}
}
[].slice.call(arguments, i).forEach(function(obj){
var src, copy, isArray, clone;
if(obj === target){
return;
}
if(deep && obj instanceof Array){
target = dedup ? angular.unique(target.concat(obj)) : target.concat(obj);
}
else{
for(var key in obj){
src = target[key];
copy = obj[key];
if(target === copy || src === copy){
continue;
}
if((isArray = copy instanceof Array) || deep && copy && (angular.isPlainObject(copy))){
if(isArray){
clone = (src && src instanceof Array) ? src : [];
}else{
clone = (src && angular.isPlainObject(src)) ? src : {};
}
isArray = false;
if(dedup){
target[key] = angular.extend(deep, dedup, clone, copy);
}else{
target[key] = angular.extend(deep, clone, copy);
}
}
else if(copy !== undefined){
target[key] = copy;
}
}
}
});
return target;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment