%PDF- %PDF-
| Direktori : /home/opti/workflow/node_modules/underscore.deep/ |
| Current File : /home/opti/workflow/node_modules/underscore.deep/underscore.deep.coffee |
_ = require 'underscore'
module.exports =
deepKeys: deepKeys = (obj) ->
throw new Error "deepKeys must be called on an object, not '#{obj}'" unless isPlainObject obj
# In the base case where obj is empty, _.map(obj, ...) will produce []
_.flatten _.map obj, (v, k) ->
if isPlainObject(v) and not _.isEmpty(v)
_.map deepKeys(v), (subkey) -> "#{k}.#{subkey}"
else
[k]
# http://stackoverflow.com/questions/4459928/how-to-deep-clone-in-javascript
# This is mostly the same as the accepted answer, just shorter.
# However, whereas they convert instances of String, etc. to the literal, we create a new instance
# (`return type object for type` -> `return new type object for type`). This also allows us to
# handle Dates in the same statement.
# TODO: Support all core objects in js: Array, Boolean, Date, Function, Math, Number, RegExp, and
# String. Currently missing Function, Math, and RegExp.
deepClone: deepClone = (object) ->
return object if not object?
# normalizing primitives e.g. if someone did new String('aaa'), or new Number('444');
return new type object for type in [Date, Number, String, Boolean] when object instanceof type
# for arrays clone each element of it
return _(object).map deepClone if _(object).isArray()
# primitives like numbers, strings and stuff
return object if not _(object).isObject()
# if this is a DOM element
return object.cloneNode true if object.nodeType and _(object.cloneNode).isFunction()
# a class of some sort - we can't handle this, so we just return it
return object unless object.constructor is {}.constructor
# it is an object literal
mapValues object, deepClone
# like "of" operator except can recurse on keys with dot notation
deepHas: (obj, keys) ->
helper = (obj, keys) ->
if (keys.length is 0) or (not _.isObject obj)
false
else if keys.length is 1
_.first(keys) of obj
else
helper obj[_.first(keys)], _.rest(keys)
helper obj, if _.isArray keys then keys else keys.split('.')
deepOmit: (obj, keys) ->
unless isPlainObject obj
throw new Error "deepOmit must be called on an object, not '#{obj}'"
deepOmitOne = (obj, key) ->
helper = (obj, key_arr) ->
switch
when _.isEmpty key_arr then obj
when key_arr.length is 1 then _.omit obj, _.first key_arr
when not isPlainObject obj[_.first key_arr] then obj
else
_.extend {}, obj, _.object [_.first key_arr], [
helper obj[_.first key_arr], _.rest key_arr
]
helper obj, key.split('.')
_.reduce keys, deepOmitOne, obj
deepPick: do ->
deepGet = (obj, key) ->
helper = (obj, key_arr) ->
if key_arr.length is 1
obj?[_.first key_arr]
else
helper obj[_.first key_arr], _.rest key_arr
helper obj, key.split('.')
(obj, keys) ->
unless isPlainObject obj
throw new Error "deepPick must be called on an object, not '#{obj}'"
flat_new_obj = _.reduce keys, (new_obj, key) ->
val = deepGet obj, key
new_obj[key] = val if val isnt undefined
new_obj
, {}
deepFromFlat flat_new_obj
deepDelete: deepDelete = (obj, key) ->
return if not key? or not obj?
key = key.split '.' if not _(key).isArray()
if key.length is 1
delete obj[key]
return
deepDelete obj[key[0]], key.slice(1, key.length)
# Like _.extend, except instead of overwriting all nested objects, it extends
# each leaf of `obj` with the value at the corresponding leaf of `ext`.
# Note: this function is pure - it returns a new object instead of mutating
# the original object. If you must have a mutative version, pass true as the
# third `mutate` parameter.
deepExtend: deepExtend = (obj, ext, mutate) ->
_.reduce ext, (acc, val, key) ->
acc[key] =
if (key of obj) and isPlainObject(obj[key]) and isPlainObject(val)
then deepExtend obj[key], val
else val
acc
, if mutate then obj else _.clone obj
isPlainObject: isPlainObject = (value) -> value?.constructor is {}.constructor
deepToFlat: (obj) ->
res = {}
recurse = (obj, current) ->
for key of obj
value = obj[key]
newKey = ((if current then current + "." + key else key)) # joined key with dot
if value and isPlainObject value
recurse value, newKey # it's a nested object, so do it again
else
res[newKey] = value # it's not an object, so set the property
recurse obj
res
# Takes an object with keys with dot.notation and deepens it into dot{notation:{}}
deepFromFlat: deepFromFlat = (o) ->
oo = {}
t = undefined
parts = undefined
part = undefined
for k of o
t = oo
parts = k.split(".")
key = parts.pop()
while parts.length
part = parts.shift()
t = t[part] = t[part] or {}
t[key] = o[k]
oo
# Takes an object and replaces each of its values with the result of a
# function applied to that value (and its key).
mapValues: mapValues = (obj, f_val) ->
unless isPlainObject obj
throw new Error "mapValues must be called on an object, not '#{obj}'"
_.object _.keys(obj), _.map(obj, f_val)
deepMapValues: deepMapValues = (obj, f) ->
unless isPlainObject obj
throw new Error "deepMapValues must be called on an object, not '#{obj}'"
mapValues obj, (v, k) ->
if isPlainObject v
deepMapValues v, (subv, subk) ->
f subv, "#{k}.#{subk}"
else
f v, k
# note that the function takes a key and optionally a value, not the usual
# mapping function pattern of taking a value and optionally a key
mapKeys: mapKeys = (obj, f_val) ->
unless isPlainObject obj
throw new Error "mapKeys must be called on an object, not '#{obj}'"
_.object _.map(obj, (v,k) -> f_val k,v), _.values obj