underscore.deep
Advanced tools
Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "underscore.deep", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Underscore mixins for deeply nested objects", | ||
@@ -5,0 +5,0 @@ "main": "underscore.deep.js", |
@@ -7,3 +7,3 @@ # underscore.deep | ||
This README is written in [Literate CoffeeScript](http://coffeescript.org/#literate) as a [Mocha](http://visionmedia.github.io/mocha/) test suite, so you can execute all of the examples - just run: | ||
``` | ||
@@ -30,3 +30,5 @@ make README.coffee.md | ||
describe 'underscore.deep', -> | ||
{assert, _} = require './test/readmeHelpers' | ||
assert = require 'assert' | ||
_ = require 'underscore' | ||
_.mixin require './underscore.deep' | ||
@@ -51,7 +53,7 @@ ### _.deepToFlat(obj) | ||
age: 33 | ||
), | ||
), | ||
'user1.name.first': 'Deep' | ||
'user1.name.last': 'Blue' | ||
'user1.age': '33' | ||
### _.deepFromFlat(obj) | ||
@@ -128,8 +130,161 @@ | ||
### _.deepHas | ||
### _.deepHas(obj, key) | ||
TODO | ||
Takes an object `obj` and a string `key` (which should be a dot-notation key) and returns true if `obj` has a nested field named `key`. | ||
### _.deepKeys | ||
describe '_.deepHas', -> | ||
TODO | ||
obj = we: have: to: go: 'deeper' | ||
it 'returns true if a regular key exists', -> | ||
assert.equal _.deepHas(obj, 'we'), true | ||
it 'returns true if the deep key exists', -> | ||
assert.equal _.deepHas(obj, 'we.have'), true | ||
assert.equal _.deepHas(obj, 'we.have.to'), true | ||
assert.equal _.deepHas(obj, 'we.have.to.go'), true | ||
it 'returns false if the deep key does not exist', -> | ||
assert.equal _.deepHas(obj, 'we.have.to.goop'), false | ||
it 'is not equivalent to the composition of _.has and _.deepToFlat', -> | ||
assert.equal _.deepHas(obj, 'we.have.to.go'), _.has(_.deepToFlat(obj), 'we.have.to.go') | ||
assert.equal _.deepHas(obj, 'we.have.to.goop'), _.has(_.deepToFlat(obj), 'we.have.to.goop') | ||
assert.notEqual _.deepHas(obj, 'we'), _.has(_.deepToFlat(obj), 'we') | ||
### _.deepKeys(obj) | ||
Takes an object and returns all of its nested keys in dot-notation. | ||
If you think of a deeply-nested object as a tree, then it will return the paths to all of the tree's leaves. That means it won't return intermediate keys. As a consequence, `_.deepHas(obj, key)` is not equivalent to `_.contains _.deepKeys(obj), key`. | ||
describe '_.deepKeys', -> | ||
obj = | ||
node1: | ||
leaf1: 1 | ||
node2: | ||
node3: | ||
leaf2: 2 | ||
leaf3: 3 | ||
it 'returns dot-notation keys for only the leaf fields of an object', -> | ||
assert.deepEqual _.deepKeys(obj), [ | ||
'node1.leaf1' | ||
'node2.node3.leaf2' | ||
'node2.node3.leaf3' | ||
] | ||
it 'is equivalent to the composition of _.keys and _.deepToFlat', -> | ||
assert.deepEqual _.deepKeys(obj), _.keys(_.deepToFlat obj) | ||
it 'does not make _.deepHas equivalent to the composition of _.contains and _.deepKeys', -> | ||
assert.notDeepEqual _.contains(_.deepKeys(obj), 'node1'), _.has(obj, 'node1') | ||
### _.deepExtend(destination, source, mutate = false) | ||
Takes an object `destination` and an object `source` and creates a new object with all the deep fields of `destination` and all the deep fields of `source`. Any deep fields with the same deep key in `destination` and `source` will have the value from `source` (so `source` fields overwrite `destination` fields). | ||
Unlike `_.extend`, `_.deepExtend` is pure, so the original objects `destination` and `source` will not be modified. If you really want to mutate `destination` by adding the deep fields of `source`, pass `true` as the third parameter `mutate`. | ||
describe '_.deepExtend', -> | ||
destination = | ||
name: 'heaven' | ||
angels: | ||
michael: true | ||
it 'combines all the deep fields of destination and source', -> | ||
assert.deepEqual _.deepExtend(destination, { angels: gabriel: false }), | ||
name: 'heaven' | ||
angels: | ||
michael: true | ||
gabriel: false | ||
it 'overwrites fields of destination with fields from source', -> | ||
assert.deepEqual _.deepExtend(destination, { angels: michael: false }), | ||
name: 'heaven' | ||
angels: | ||
michael: false | ||
it 'does not mutate the input objects', -> | ||
assert.notStrictEqual destination, _.deepExtend(destination, { name: 'hell' }) | ||
it 'is equivalent to a weird composition of _.deepFromFlat, _.extend, and _.deepToFlat', -> | ||
assert.deepEqual _.deepExtend(destination, { angels: gabriel: false }), | ||
_.deepFromFlat _.extend _.deepToFlat(destination), _.deepToFlat({ angels: gabriel: false }) | ||
### _.deepMapValues(obj, func) | ||
Like [_.mapValues](#_mapvaluesobj-func), but for deep objects. Constructs a new object by applying function `func` to the value for every deep field in object `obj`. | ||
describe '_.deepMapValues', -> | ||
obj = | ||
values: | ||
empathy: true | ||
responsibility: false | ||
it 'creates an object by applying func to each deep value in obj', -> | ||
assert.deepEqual _.deepMapValues(obj, (v) -> not v), | ||
values: | ||
empathy: false | ||
responsibility: true | ||
it 'is equivalent to the composition of _.deepFromFlat, _.mapValues, and _.deepToFlat', -> | ||
assert.deepEqual _.deepMapValues(obj, (v) -> String v), | ||
_.deepFromFlat _.mapValues _.deepToFlat(obj), (v) -> String v | ||
## Non-deep Helpers | ||
Someday these will probably be moved into their own library, but for now they live here. | ||
### _.isPlainObject(val) | ||
Takes a value `val` and returns `true` if it's a vanilla JS object (i.e. not an instance of any built-in or custom class). Otherwise returns false. | ||
describe '_.isPlainObject', -> | ||
it 'returns true for vanilla objects', -> | ||
assert.equal _.isPlainObject({}), true | ||
assert.equal _.isPlainObject({ vanilla: 'is so plain' }), true | ||
it 'returns false for other values', -> | ||
assert.equal _.isPlainObject(1), false | ||
assert.equal _.isPlainObject('chocolate'), false | ||
assert.equal _.isPlainObject(new Date()), false | ||
### _.mapValues(obj, func) | ||
Takes an object `obj` and a function `func` and constructs a new object by applying `func` to every value in `obj`. `func` receives two arguments, the value and the key for that value. | ||
Some have [described](https://github.com/jashkenas/underscore/issues/220#issuecomment-12112759) this function as "the fundamental map over dictionaries." Others have [said](https://github.com/jashkenas/underscore/issues/220#issuecomment-1470150) its not "mainstream enough to deserve to make it into Underscore proper." We take no stance in the debate, but we have to admit we use it on the daily. | ||
describe '_.mapValues', -> | ||
obj = | ||
respect: 1 | ||
fairness: 2 | ||
it 'creates an object by applying func to each value in obj', -> | ||
assert.deepEqual _.mapValues(obj, (v) -> v * 10), | ||
respect: 10 | ||
fairness: 20 | ||
### _.mapKeys(obj, func) | ||
Exactly like [_.mapValues](#_mapvaluesobj-func) but for keys. | ||
Note that the function takes a function takes a key and optionally a value, not the usual mapping function pattern of taking a value and optionally a key | ||
describe '_.mapKeys', -> | ||
obj = | ||
animate: 1 | ||
charge: 2 | ||
it 'creates an object by applying func to each key in obj', -> | ||
assert.deepEqual _.mapKeys(obj, (key) -> 're' + key), | ||
reanimate: 1 | ||
recharge: 2 | ||
it 'creates an object by applying func to each key, val in obj', -> | ||
assert.deepEqual _.mapKeys(obj, (key, val) -> 're' + key + val), | ||
reanimate1: 1 | ||
recharge2: 2 |
// Generated by CoffeeScript 1.6.3 | ||
var deepClone, deepDelete, deepExtend, deepKeys, deepMapValues, isPlainObject, mapValues, _; | ||
var deepClone, deepDelete, deepExtend, deepKeys, deepMapValues, isPlainObject, mapKeys, mapValues, _; | ||
@@ -7,17 +7,15 @@ _ = require('underscore'); | ||
module.exports = { | ||
deepKeys: deepKeys = function(obj, prefix) { | ||
var key, keys, val; | ||
if (prefix == null) { | ||
prefix = ''; | ||
deepKeys: deepKeys = function(obj) { | ||
if (!isPlainObject(obj)) { | ||
throw new Error("deepKeys must be called on an object, not '" + obj + "'"); | ||
} | ||
keys = []; | ||
for (key in obj) { | ||
val = obj[key]; | ||
if (_.isObject(val) && !_.isArray(val) && !_.isEmpty(val)) { | ||
keys = _.union(keys, deepKeys(val, "" + prefix + key + ".")); | ||
return _.flatten(_.map(obj, function(v, k) { | ||
if (isPlainObject(v) && !_.isEmpty(v)) { | ||
return _.map(deepKeys(v), function(subkey) { | ||
return "" + k + "." + subkey; | ||
}); | ||
} else { | ||
keys.push("" + prefix + key); | ||
return [k]; | ||
} | ||
} | ||
return keys; | ||
})); | ||
}, | ||
@@ -125,13 +123,27 @@ deepClone: deepClone = function(object) { | ||
mapValues: mapValues = function(obj, f_val) { | ||
if (!_.isPlainObject(obj)) { | ||
throw new Error("mapValues must be called on an object, not '" + obj + "'"); | ||
} | ||
return _.object(_.keys(obj), _.map(obj, f_val)); | ||
}, | ||
deepMapValues: deepMapValues = function(obj, f) { | ||
if (isPlainObject(obj)) { | ||
return mapValues(obj, function(v) { | ||
if (!_.isPlainObject(obj)) { | ||
throw new Error("deepMapValues must be called on an object, not '" + obj + "'"); | ||
} | ||
return mapValues(obj, function(v) { | ||
if (_.isPlainObject(v)) { | ||
return deepMapValues(v, f); | ||
}); | ||
} else { | ||
return f(obj); | ||
} else { | ||
return f(v); | ||
} | ||
}); | ||
}, | ||
mapKeys: mapKeys = function(obj, f_val) { | ||
if (!_.isPlainObject(obj)) { | ||
throw new Error("mapKeys must be called on an object, not '" + obj + "'"); | ||
} | ||
return _.object(_.map(obj, function(v, k) { | ||
return f_val(k, v); | ||
}), _.values(obj)); | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
29550
18
145
287