Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save deleted/4735973 to your computer and use it in GitHub Desktop.
Save deleted/4735973 to your computer and use it in GitHub Desktop.
# Create a deep copy of an object. - Based on https://github.com/documentcloud/underscore/pull/595
deepClone = (obj) ->
if !_.isObject obj or _.isFunction obj then return obj
if _.isDate obj then return new Date do obj.getTime
if _.isRegExp obj then return new RegExp obj.source, obj.toString().replace(/.*\//, "")
isArr = _.isArray obj or _.isArguments obj
func = (memo, value, key) ->
if isArr then memo.push deepClone value
else memo[key] = deepClone value
return memo;
return _.reduce obj, func, if isArr then [] else {}
# Is a given value a basic Object? i.e.: {} || new Object()
isBasicObject = (object) ->
(object.prototype is {}.prototype or object.prototype is Object.prototype) and _.isObject(object) and not _.isArray(object) and not _.isFunction(object) and not _.isDate(object) and not _.isRegExp(object) and not _.isArguments(object)
# Returns a list of the names of every object in an object — that is to say, the name of every property of the object that is an object.
basicObjects = (object) ->
_.filter _.keys(object), (key) -> isBasicObject object[key]
# Returns a list of the names of every array in an object — that is to say, the name of every property of the object that is an array.
arrays = (object) ->
_.filter(_.keys(object), (key) -> _.isArray object[key])
# Copy and combine all of the properties in the source objects over to the destination object and return the destination object. This method will recursively copy shared properties which are also objects and combine arrays.
deepExtendCouple = (destination, source, maxDepth=20) ->
if maxDepth <= 0
console.warn '_.deepExtend(): Maximum depth of recursion hit.'
return _.extend destination, source
sharedObjectKeys = _.intersection(basicObjects(destination), basicObjects(source))
recurse = (key) ->
source[key] = deepExtendCouple destination[key], source[key], maxDepth-1
recurse sharedObjectKey for sharedObjectKey in sharedObjectKeys
sharedArrayKeys = _.intersection(arrays(destination), arrays(source))
combine = (key) ->
source[key] = _.union destination[key], source[key]
combine sharedArrayKey for sharedArrayKey in sharedArrayKeys
_.extend destination, source
# Copy and combine all of the properties in the supplied objects from right to left and return the combined object. This method will recursively copy shared properties which are also objects and combine arrays.
deepExtend = (objects..., maxDepth) ->
if !_.isNumber maxDepth
objects.push maxDepth
maxDepth = 20
if objects.length <= 1 then return objects[0]
if maxDepth <= 0 then return _.extend.apply this, objects
finalObj = do objects.shift
while objects.length > 0
finalObj = deepExtendCouple(finalObj, deepClone(do objects.shift), maxDepth)
return finalObj
_.mixin
deepClone: deepClone
isBasicObject: isBasicObject
basicObjects: basicObjects
arrays: arrays
deepExtend: deepExtend
foo =
array: [3, 4, 5, 1, 'rawr', 'foo']
name:
first: "Earl"
last: "Jones"
job: "Winning"
friends:
"01234":
name:
first: "Whodat"
bar =
array: [1, 2, 3, 'rawr', 'zoom']
name:
middle: "Samus"
job: "Farmer"
friends:
"01234":
name:
last: "Mook"
friends:
deep:
connection:
going:
all:
the:
combined: "Heyo!"
way:
down:
depth: "Too Deep?"
hear: "Not Too Deep"
blam =
name:
first: "Eddie"
job: "Mugger"
friends:
"01234":
name:
first: "Honorary"
friends:
deep:
connection:
going:
all:
the:
way:
down:
depth: "Way Too Deep!"
see: "What I mean?"
"43210":
name:
first: "Guy"
last: "Rogers"
# Use deepClone when testing because deepExtend will modify the first parameter
console.log(_.deepClone(foo),_.deepClone(bar),_.deepClone(blam))
console.log('default', _.deepExtend(_.deepClone(foo), bar, blam))
console.log('0', _.deepExtend(_.deepClone(foo), bar, blam, 0))
console.log('20', _.deepExtend(_.deepClone(foo), bar, blam, 20))
console.log('mixed-default', _.deepExtend(_.deepClone(blam), foo, bar, 20))
console.log('mixed-0', _.deepExtend(_.deepClone(blam), foo, bar, 0))
console.log('mixed-20', _.deepExtend(_.deepClone(blam), foo, bar, 20))
console.log(foo,bar,blam)
(function() {
var arrays, basicObjects, deepClone, deepExtend, deepExtendCouple, isBasicObject,
__slice = [].slice;
deepClone = function(obj) {
var func, isArr;
if (!_.isObject(obj || _.isFunction(obj))) {
return obj;
}
if (_.isDate(obj)) {
return new Date(obj.getTime());
}
if (_.isRegExp(obj)) {
return new RegExp(obj.source, obj.toString().replace(/.*\//, ""));
}
isArr = _.isArray(obj || _.isArguments(obj));
func = function(memo, value, key) {
if (isArr) {
memo.push(deepClone(value));
} else {
memo[key] = deepClone(value);
}
return memo;
};
return _.reduce(obj, func, isArr ? [] : {});
};
isBasicObject = function(object) {
return (object.prototype === {}.prototype || object.prototype === Object.prototype) && _.isObject(object) && !_.isArray(object) && !_.isFunction(object) && !_.isDate(object) && !_.isRegExp(object) && !_.isArguments(object);
};
basicObjects = function(object) {
return _.filter(_.keys(object), function(key) {
return isBasicObject(object[key]);
});
};
arrays = function(object) {
return _.filter(_.keys(object), function(key) {
return _.isArray(object[key]);
});
};
deepExtendCouple = function(destination, source, maxDepth) {
var combine, recurse, sharedArrayKey, sharedArrayKeys, sharedObjectKey, sharedObjectKeys, _i, _j, _len, _len1;
if (maxDepth == null) {
maxDepth = 20;
}
if (maxDepth <= 0) {
console.warn('_.deepExtend(): Maximum depth of recursion hit.');
return _.extend(destination, source);
}
sharedObjectKeys = _.intersection(basicObjects(destination), basicObjects(source));
recurse = function(key) {
return source[key] = deepExtendCouple(destination[key], source[key], maxDepth - 1);
};
for (_i = 0, _len = sharedObjectKeys.length; _i < _len; _i++) {
sharedObjectKey = sharedObjectKeys[_i];
recurse(sharedObjectKey);
}
sharedArrayKeys = _.intersection(arrays(destination), arrays(source));
combine = function(key) {
return source[key] = _.union(destination[key], source[key]);
};
for (_j = 0, _len1 = sharedArrayKeys.length; _j < _len1; _j++) {
sharedArrayKey = sharedArrayKeys[_j];
combine(sharedArrayKey);
}
return _.extend(destination, source);
};
deepExtend = function() {
var finalObj, maxDepth, objects, _i;
objects = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), maxDepth = arguments[_i++];
if (!_.isNumber(maxDepth)) {
objects.push(maxDepth);
maxDepth = 20;
}
if (objects.length <= 1) {
return objects[0];
}
if (maxDepth <= 0) {
return _.extend.apply(this, objects);
}
finalObj = objects.shift();
while (objects.length > 0) {
finalObj = deepExtendCouple(finalObj, deepClone(objects.shift()), maxDepth);
}
return finalObj;
};
_.mixin({
deepClone: deepClone,
isBasicObject: isBasicObject,
basicObjects: basicObjects,
arrays: arrays,
deepExtend: deepExtend
});
}).call(this);
var user = { "name": { "first": "Earl" }, "friends": ["0","1","3"], "job": "Mad Scientist" };
var userUpdate = { "name": { "last": "Duncan" }, "friends": ["0","1","2"] };
_.deepExtend(user, userUpdate);
// results in user equalling:
// { "name": { "first": "Earl", "last": "Duncan" }, "friends": ["0","1","2","3"], "job": "Mad Scientist" }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment