Last active
November 28, 2020 23:55
-
-
Save ElliotChong/3861963 to your computer and use it in GitHub Desktop.
Copy all of the properties in the source objects over to the destination object, and return the destination object. This method will recursively copy mutual properties which are also objects.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Create a deep copy of an object. - CoffeeScript conversion of @cederberg's deepClone implementation 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var user = { "name": { "first": "Earl" }, "friends": ["0","1","3"], "job": "Mad Scientist", "pets": { "dog": "Ralph" } }; | |
var userUpdate = { "name": { "last": "Duncan" }, "friends": ["6","9"], "job": "Happy Scientist", "pets": { "cat": "Judy" }, "city": "Portlandia" }; | |
_.deepExtend(user, userUpdate); | |
// results in user equalling: | |
// { "name": { "first": "Earl", "last": "Duncan" }, "friends": ["0","1","3","6","9"], "job": "Happy Scientist", "pets": { "dog": "Ralph", "cat": "Judy" }, "city": "Portlandia" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var user = { "name": { "first": "Earl" }, "friends": ["0","1","3"], "job": "Mad Scientist", "pets": { "dog": "Ralph" } }; | |
var userUpdate = { "name": { "last": "Duncan" }, "friends": ["6","9"], "job": "Happy Scientist", "pets": { "cat": "Judy" }, "city": "Portlandia" }; | |
_.extend(user, userUpdate); | |
// results in user equalling: | |
// { "name": { "last": "Duncan" }, "friends": ["6","9"], "job": "Happy Scientist", "pets": { "cat": "Judy" }, "city": "Portlandia" } |
What's the license on this?
As dschi mentioned for the coffescript version, the js version should be
if (!_.isObject(obj) || _.isFunction(obj)) {
instead of
if (!_.isObject(obj || _.isFunction(obj))) {
What about this:
var clone = JSON.parse(JSON.stringify(obj));
@AndrewEastwood Nice deepClone replacement! You'll lose Date and RegEx handling, but if you're not using those data types that should work well.
@chrisritter Thanks for asking! License is WTFPL.
http://en.wikipedia.org/wiki/WTFPL
'null' values don't work. I get an error in isBasicObject:
TypeError: Cannot read property 'prototype' of null
The problem can be solved by switching the order of the first two clauses in the logical expression in isBasicObject().
_.isObject(object) && (object.prototype === {}.prototype || object.prototype === Object.prototype)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
should be