Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save michsch/4749997 to your computer and use it in GitHub Desktop.
Save michsch/4749997 to your computer and use it in GitHub Desktop.
###jshint devel:true
###
###global require, define, _, module
###
###*
* Some new useful underscore mixins
* originally by echong: https://gist.github.com/echong/3861963#file-underscore-mixin-deepextend-coffee
###
( ( root, factory ) ->
"use strict"
# CommonJS
if typeof _ is 'function' and typeof exports is 'object'
module.exports = factory();
# AMD
else if typeof define is 'function' and define.amd
define(['underscore'], ( _ ) ->
_.mixin factory()
return _
)
# Browser
else if typeof _ is 'function'
root._.mixin factory()
)( ( typeof window is 'object' and window ) || this, () ->
"use strict"
exports = exports || {}
# 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) ->
maxDepth = 20 if maxDepth is undefined
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
exports =
deepClone: deepClone
isBasicObject: isBasicObject
basicObjects: basicObjects
arrays: arrays
deepExtend: deepExtend
return exports
)
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)
/*jshint devel:true*/
/*global require, define, _, module*/
/**
* Some new useful underscore mixins
* originally by echong: https://gist.github.com/echong/3861963#file-underscore-mixin-deepextend-coffee
*/
(function() {
var __slice = [].slice;
(function(root, factory) {
"use strict";
if (typeof _ === 'function' && typeof exports === 'object') {
return module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
return define(['underscore'], function(_) {
_.mixin(factory());
return _;
});
} else if (typeof _ === 'function') {
return root._.mixin(factory());
}
})((typeof window === 'object' && window) || this, function() {
"use strict";
var arrays, basicObjects, deepClone, deepExtend, deepExtendCouple, exports, isBasicObject;
exports = exports || {};
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 === void 0) {
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;
};
exports = {
deepClone: deepClone,
isBasicObject: isBasicObject,
basicObjects: basicObjects,
arrays: arrays,
deepExtend: deepExtend
};
return exports;
});
}).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