initial commit of actions

This commit is contained in:
Dominik Polakovics Polakovics 2026-01-31 18:56:04 +01:00
commit 949ece5785
44660 changed files with 12034344 additions and 0 deletions

View file

@ -0,0 +1,94 @@
# ChangeLog
## 2020-09-10 Version 2.1.4
* [[`94b9eb8a2d`](https://github.com/rhalff/dot-object/commit/94b9eb8a2d)] - Fix parsing of array paths for non standard separators (Fixed by boidolr #58)
## 2020-02-16 Version 2.1.3
* fix possible pollution of prototype for paths containing __proto__
## 2019-11-02 Version 2.1.1
* fix undefined key with root level array.
## 2019-11-02 Version 2.1.1
* Wrong array conversion when brackets are used (Reported by vladshcherbin #27)
## 2019-11-02 Version 2.1.0
* fix delete function not being wrapped. (Reported by murphyke #40)
## 2019-11-02 Version 2.0.0
* [[`2cb41bbd1b`](https://github.com/rhalff/dot-object/commit/2cb41bbd1b)] - Add useBrackets option for the .dot() method (by z1m1n #42)
* dot() now writes brackets by default (possibly breaking change).
Use Dot.useBrackets = false to keep the old behavior
## 2019-07-29 Version 1.9.0
* allows allow to process root level property using dot.object
## 2019-07-18 Version 1.8.1
* always allow to set root level property using dot.str
## 2019-07-18 Version 1.8.0
* [[`cdc691424b`](https://github.com/rhalff/dot-object/commit/cdc691424b)] - Options to remove array elements and reindex the array. (Fixed by MechJosh0 #36)
## 2018-10-26 Version 1.7.1
* [[`e1bb99c83e`](https://github.com/rhalff/dot-object/commit/e1bb99c83e)] - Fix isIndex numeric key matching. (Fixed by mrdivyansh #31)
## 2017-09-20 Version 1.7.0
* let .dot and .object understand empty objects / arrays
## 2017-07-27 Version 1.6.0
* implemented keepArray
## 2016-09-29 Version 1.5.4
* update dist
## 2016-09-29, Version 1.5.3
* Fix override bug in str()
## 2016-08-04, Version 1.5.0
* [[`a7e948f2fa`](https://github.com/rhalff/dot-object/commit/a7e948f2fa)] - Ensure we only process true Arrays and Objects. (Fixed by MechJosh0 #15)
## 2016-02-14, Version 1.4.0
* add transform() method
* use [standard](https://github.com/feross/standard/) style
## 2015-11-17, Version 1.3.1
* [[`e46da6ffc0`](https://github.com/rhalff/dot-object/commit/e46da6ffc0)] - Fix deep array bug. (Reported by ami44 #10)
## 2015-10-01, Version 1.3.0
* [[`baa42022bf`](https://github.com/rhalff/dot-object/commit/baa42022bf)] - Adds a parameter (useArray) to allow converting object without using arrays. (Thomas Moiron)
## 2015-09-07, Version 1.2.0
* allow override even when target is non-empty object
* also return the object when using object() or str()
## 2015-08-08, Version 1.1.0
* Also let .object() understand array notation.
## 2015-08-03, Version 1.0.0
* Convert object to dotted-key/value pairs
## 2015-04-15, Version 0.11.0
* Add support for bracket notation
## 2015-03-22, Version 0.10.0
* Make return value optional for Object/Array modifier(s)
* Add modifier option to move(), transfer() & copy()
## 2015-03-21, Version 0.9.0
* support multiple replacements/deletions (cli)
* add del/remove method
* improve bower build
## 2015-03-09, Version 0.8.1
* [[`679f87590f`](https://github.com/rhalff/dot-object/commit/679f87590f)] - add to bower
* [[`9a026982d8`](https://github.com/rhalff/dot-object/commit/9a026982d8)] - consider amd or attaching to window
## 2015-03-06, Version 0.8.0
* [[`5ce0fe8836`](https://github.com/rhalff/dot-object/commit/5ce0fe8836)] - Simplify API
## 2015-03-06, Version 0.7.0
* [[`c4658c386f`](https://github.com/rhalff/dot-object/commit/c4658c386f)] - Look for properties down in the prototype chain. (Fer Uría)
* [[`a45c4a7982`](https://github.com/rhalff/dot-object/commit/a45c4a7982)] - Fix picking a null property with a dotted value. (Fer Uría)

View file

@ -0,0 +1,20 @@
Copyright (c) 2013 Rob Halff
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,433 @@
[![Build Status](https://travis-ci.org/rhalff/dot-object.png)](https://travis-ci.org/rhalff/dot-object)
Dot-Object
========
Dot-Object makes it possible to transform javascript objects using dot notation.
### Installation
Install from npm:
```
npm install dot-object --save
```
Install from bower:
```
bower install dot-object --save
```
### Download
* Development version: https://unpkg.com/dot-object/dist/dot-object.js *Uncompressed with Comments*
* Production version: https://unpkg.com/dot-object/dist/dot-object.min.js *Minified*
## Usage
#### Move a property within one object to another location
```javascript
var dot = require('dot-object');
var obj = {
'first_name': 'John',
'last_name': 'Doe'
};
dot.move('first_name', 'contact.firstname', obj);
dot.move('last_name', 'contact.lastname', obj);
console.log(obj);
{
contact: {
firstname: 'John',
lastname: 'Doe'
}
}
```
#### Copy property from one object to another
```javascript
var dot = require('dot-object');
var src = {
name: 'John',
stuff: {
phone: {
brand: 'iphone',
version: 6
}
}
};
var tgt = {name: 'Brandon'};
dot.copy('stuff.phone', 'wanna.haves.phone', src, tgt);
console.log(tgt);
{
name: 'Brandon',
wanna: {
haves: {
phone: {
brand: 'iphone',
version: 6
}
}
}
}
```
#### Transfer property from one object to another
Does the same as copy but removes the value from the source object:
```javascript
dot.transfer('stuff.phone', 'wanna.haves.phone', src, tgt);
// src: {"name":"John","stuff":{}}
// tgt: {"name":"Brandon","wanna":{"haves":{"phone":{"brand":"iphone","version":6}}}
```
#### Expand to an object
```javascript
var dot = require('dot-object');
var row = {
'id': 2,
'contact.name.first': 'John',
'contact.name.last': 'Doe',
'contact.email': 'example@gmail.com',
'contact.info.about.me': 'classified',
'devices[0]': 'mobile',
'devices[1]': 'laptop',
'some.other.things.0': 'this',
'some.other.things.1': 'that'
};
dot.object(row);
console.log(row);
{
"id": 2,
"contact": {
"name": {
"first": "John",
"last": "Doe"
},
"email": "example@gmail.com",
"info": {
"about": {
"me": "classified"
}
}
},
"devices": [
"mobile",
"laptop"
],
"some": {
"other": {
"things": [
"this",
"that"
]
}
}
}
```
To convert manually per string use:
```javascript
var dot = require('dot-object');
var tgt = { val: 'test' };
dot.str('this.is.my.string', 'value', tgt);
console.log(tgt);
{
"val": "test",
"this": {
"is": {
"my": {
"string": "value"
}
}
}
}
```
#### Pick a value using dot notation:
Picks a value from the object without removing it.
```js
var dot = require('dot-object');
var obj = {
some: {
nested: {
value: 'Hi there!'
}
}
};
var val = dot.pick('some.nested.value', obj);
console.log(val);
Hi there!
```
#### Delete/Remove a value using dot notation:
Remove and delete mostly behave the same, but in case of a path addressing array items:
- `delete` will re-index the array.
- `remove` will retain array indexes
```js
var dot = require('dot-object');
var obj = {
a: 'Hi There!',
nested: {
array: [
'Veni',
'Vidi',
'Vici',
]
}
};
var val = dot.delete('a', obj);
console.log(val);
Hi There!
// To remove an item and directly update any array indexes use:
var val = dot.delete('nested.array[1]', obj);
console.log(val);
Vidi
// Remove a value but retain array indexes.
var val = dot.remove('nested.array[1]', obj);
// To remove multiple paths at once:
var val = dot.remove(['nested.array[0]', 'nested.array[2]'], obj);
```
### Using modifiers
You can use modifiers to translate values on the fly.
This example uses the [underscore.string](https://github.com/epeli/underscore.string) library.
```javascript
var dot = require('dot-object');
var _s = require('underscore.string');
var row = {
'nr': 200,
'doc.name': ' My Document '
};
var mods = {
"doc.name": [_s.trim, _s.underscored],
};
dot.object(row, mods);
console.log(row);
```
```
{
"nr": 200,
"doc": {
"name": "my_document"
}
}
```
Or using .str() directy:
```javascript
var dot = require('dot-object');
var _s = require('underscore.string');
var obj = { id: 100 };
// use one modifier
dot.str('my.title', 'this is my title', obj, _s.slugify);
// multiple modifiers
dot.str('my.title', ' this is my title ', obj, [_s.trim, _s.slugify]);
console.log(obj);
```
Result:
```json
{
"id": 100,
"my": {
"title": "this-is-my-title"
}
}
```
#### Transform object
```javascript
var dot = require('dot-object');
var source = {
"id": 1,
"contact": {
"firstName": "John",
"lastName": "Doe",
"email": "example@gmail.com",
}
}
var recipe = {
'id': 'nr',
'contact.firstName': 'name.first',
'contact.lastName': 'name.last',
'contact.email': 'email'
};
var tgt = {}
dot.transform(recipe, source, tgt);
// OR
var tgt = dot.transform(recipe, source);
console.log(tgt);
{
"nr": 1,
"name": {
"first": "John",
"last": "Doe"
},
"email": "example@gmail.com"
}
```
### Convert object to dotted-key/value pair
```javascript
var dot = require('dot-object');
var obj = {
id: 'my-id',
nes: { ted: { value: true } },
other: { nested: { stuff: 5 } },
some: { array: ['A', 'B'] }
};
var tgt = dot.dot(obj);
// or
var tgt = {};
dot.dot(obj, tgt);
console.log(tgt);
```
Result:
```json
{
"id": "my-id",
"nes.ted.value": true,
"other.nested.stuff": 5,
"some.array[0]": "A",
"some.array[1]": "B"
}
```
### Keep array
Set keepArray to true.
```javascript
var dot = require('dot-object');
var obj = {
id: 'my-id',
other: [1, 2, 3],
some: { array: ['A', 'B'] }
};
dot.keepArray = true;
var tgt = dot.dot(obj);
console.log(tgt);
```
Result:
```json
{
"id": "my-id",
"other": [1, 2, 3],
"some.array": ["A", "B"]
}
```
## Using a different separator
If you do not like dot notation, you are free to specify a different separator.
```javascript
var Dot = require('dot-object');
var dot = new Dot('->');
var _s = require('underscore.string');
var row = {
'nr': 200,
'doc->name': ' My Document '
};
var mods = {
"doc->name": [_s.trim, _s.underscored],
};
dot.object(row, mods);
console.log(row);
```
```
{
"nr": 200,
"doc": {
"name": "my_document"
}
}
```
## Transforming SQL results to JSON
SQL translation on the fly:
```javascript
// TODO
```
> Copyright © 2013 Rob Halff, released under the MIT license

View file

@ -0,0 +1,133 @@
#!/usr/bin/env node
// vim: set filetype=javascript:
'use strict';
var glob = require('glob');
var fs = require('fs');
/**
*
* dotob <pattern> -f require -t dependencies.npm
*
*/
var dotob = require('../index.js');
var program = require('commander');
var pkg = require('../package.json');
program
.version(pkg.version)
.usage('[options]')
.option('-p, --pattern [pattern]', 'Files pattern to match or just the file')
.option('-f, --from [path,..]', 'From path')
.option('-t, --to [path,..]', 'To path (number of replacements must match --to values)')
.option('-m, --merge', 'Merge into target')
.option('-r, --remove [path,..]', 'Remove property')
.option('-c, --dot', 'Convert object to dotted-key/value pair')
.option('-s, --show', 'show all dotted paths')
.option('-v, --verbose', 'Be verbose')
.option('-d, --dry', 'Dry run do not modify files')
.parse(process.argv);
function must(program, option) {
if(!program.hasOwnProperty(option)) {
console.log([
'The', option, 'is required'
].join(' '));
process.exit(1);
}
}
must(program, 'pattern');
if (!program.remove && !program.dot && !program.show) {
must(program, 'from');
must(program, 'to');
}
var g = glob(program.pattern);
function finish(program, file, orig, json) {
return function(err) {
if (err) {
throw err;
} else {
if (program.verbose) {
if (orig !== JSON.stringify(json)) {
console.log(file + ': updated.');
} else {
console.log(file + ': no matches.');
}
}
}
};
}
function splim(path) {
return path.split(',')
.map(function(val) { return val.trim(); });
}
function processFile(file) {
fs.readFile(file, 'utf8', function(err, contents) {
var json;
if (err) {
console.log(err);
return;
}
try {
json = JSON.parse(contents);
if(program.show) {
json = console.log(Object.keys(dotob.dot(json)).join('\n'));
process.exit()
} else if(program.dot) {
console.log(dotob.dot(json));
process.exit()
}
json = Array.isArray(json) ? json : [json];
if(program.remove) {
// support comma seperate list of removals
splim(program.remove)
.forEach(function(path) {
for (var j = 0; j < json.length; j++) {
dotob.remove(path, json[j]);
}
});
} else {
var from = splim(program.from);
var to = splim(program.to);
if (from.length === to.length) {
for (var i = 0; i < from.length; i++) {
for (var j = 0; j < json.length; j++) {
dotob.move(
from[i], to[i], json[j], program.merge
);
}
}
} else {
console.error('--from and --to parameters are not of equal length');
}
}
if(program.dry) {
console.log(json);
finish(program, file, contents, json)();
} else {
fs.writeFile(file, JSON.stringify(json, null, 2), finish(
program, file, contents, json
));
}
} catch (e) {
console.log(file + ': ');
throw(e);
}
});
}
g.on('match', processFile);

View file

@ -0,0 +1,35 @@
{
"name": "dot-object",
"version": "2.1.4",
"description": "dot-object makes it possible to transform and read (JSON) objects using dot notation.",
"main": "dist/dot-object.js",
"authors": [
{ "name": "Rob Halff", "email": "rob.halff@gmail.com" }
],
"ignore": [
"bin",
"node_modules",
"src",
"gulpfile.js",
"index.js"
],
"keywords": [
"json",
"filter",
"transform",
"dot",
"notation",
"dot"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/rhalff/dot-object.git"
},
"homepage": "https://github.com/rhalff/dot-object",
"moduleType": [
"amd",
"globals",
"node"
]
}

View file

@ -0,0 +1,600 @@
(function(global, exportName) {
'use strict'
function _process(v, mod) {
var i
var r
if (typeof mod === 'function') {
r = mod(v)
if (r !== undefined) {
v = r
}
} else if (Array.isArray(mod)) {
for (i = 0; i < mod.length; i++) {
r = mod[i](v)
if (r !== undefined) {
v = r
}
}
}
return v
}
function parseKey(key, val) {
// detect negative index notation
if (key[0] === '-' && Array.isArray(val) && /^-\d+$/.test(key)) {
return val.length + parseInt(key, 10)
}
return key
}
function isIndex(k) {
return /^\d+$/.test(k)
}
function isObject(val) {
return Object.prototype.toString.call(val) === '[object Object]'
}
function isArrayOrObject(val) {
return Object(val) === val
}
function isEmptyObject(val) {
return Object.keys(val).length === 0
}
var blacklist = ['__proto__', 'prototype', 'constructor']
var blacklistFilter = function(part) {
return blacklist.indexOf(part) === -1
}
function parsePath(path, sep) {
if (path.indexOf('[') >= 0) {
path = path.replace(/\[/g, sep).replace(/]/g, '')
}
var parts = path.split(sep)
var check = parts.filter(blacklistFilter)
if (check.length !== parts.length) {
throw Error('Refusing to update blacklisted property ' + path)
}
return parts
}
var hasOwnProperty = Object.prototype.hasOwnProperty
function DotObject(separator, override, useArray, useBrackets) {
if (!(this instanceof DotObject)) {
return new DotObject(separator, override, useArray, useBrackets)
}
if (typeof override === 'undefined') override = false
if (typeof useArray === 'undefined') useArray = true
if (typeof useBrackets === 'undefined') useBrackets = true
this.separator = separator || '.'
this.override = override
this.useArray = useArray
this.useBrackets = useBrackets
this.keepArray = false
// contains touched arrays
this.cleanup = []
}
var dotDefault = new DotObject('.', false, true, true)
function wrap(method) {
return function() {
return dotDefault[method].apply(dotDefault, arguments)
}
}
DotObject.prototype._fill = function(a, obj, v, mod) {
var k = a.shift()
if (a.length > 0) {
obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
if (!isArrayOrObject(obj[k])) {
if (this.override) {
obj[k] = {}
} else {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error(
'Trying to redefine `' + k + '` which is a ' + typeof obj[k]
)
}
return
}
}
this._fill(a, obj[k], v, mod)
} else {
if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error("Trying to redefine non-empty obj['" + k + "']")
}
return
}
obj[k] = _process(v, mod)
}
}
/**
*
* Converts an object with dotted-key/value pairs to it's expanded version
*
* Optionally transformed by a set of modifiers.
*
* Usage:
*
* var row = {
* 'nr': 200,
* 'doc.name': ' My Document '
* }
*
* var mods = {
* 'doc.name': [_s.trim, _s.underscored]
* }
*
* dot.object(row, mods)
*
* @param {Object} obj
* @param {Object} mods
*/
DotObject.prototype.object = function(obj, mods) {
var self = this
Object.keys(obj).forEach(function(k) {
var mod = mods === undefined ? null : mods[k]
// normalize array notation.
var ok = parsePath(k, self.separator).join(self.separator)
if (ok.indexOf(self.separator) !== -1) {
self._fill(ok.split(self.separator), obj, obj[k], mod)
delete obj[k]
} else {
obj[k] = _process(obj[k], mod)
}
})
return obj
}
/**
* @param {String} path dotted path
* @param {String} v value to be set
* @param {Object} obj object to be modified
* @param {Function|Array} mod optional modifier
*/
DotObject.prototype.str = function(path, v, obj, mod) {
var ok = parsePath(path, this.separator).join(this.separator)
if (path.indexOf(this.separator) !== -1) {
this._fill(ok.split(this.separator), obj, v, mod)
} else {
obj[path] = _process(v, mod)
}
return obj
}
/**
*
* Pick a value from an object using dot notation.
*
* Optionally remove the value
*
* @param {String} path
* @param {Object} obj
* @param {Boolean} remove
*/
DotObject.prototype.pick = function(path, obj, remove, reindexArray) {
var i
var keys
var val
var key
var cp
keys = parsePath(path, this.separator)
for (i = 0; i < keys.length; i++) {
key = parseKey(keys[i], obj)
if (obj && typeof obj === 'object' && key in obj) {
if (i === keys.length - 1) {
if (remove) {
val = obj[key]
if (reindexArray && Array.isArray(obj)) {
obj.splice(key, 1)
} else {
delete obj[key]
}
if (Array.isArray(obj)) {
cp = keys.slice(0, -1).join('.')
if (this.cleanup.indexOf(cp) === -1) {
this.cleanup.push(cp)
}
}
return val
} else {
return obj[key]
}
} else {
obj = obj[key]
}
} else {
return undefined
}
}
if (remove && Array.isArray(obj)) {
obj = obj.filter(function(n) {
return n !== undefined
})
}
return obj
}
/**
*
* Delete value from an object using dot notation.
*
* @param {String} path
* @param {Object} obj
* @return {any} The removed value
*/
DotObject.prototype.delete = function(path, obj) {
return this.remove(path, obj, true)
}
/**
*
* Remove value from an object using dot notation.
*
* Will remove multiple items if path is an array.
* In this case array indexes will be retained until all
* removals have been processed.
*
* Use dot.delete() to automatically re-index arrays.
*
* @param {String|Array<String>} path
* @param {Object} obj
* @param {Boolean} reindexArray
* @return {any} The removed value
*/
DotObject.prototype.remove = function(path, obj, reindexArray) {
var i
this.cleanup = []
if (Array.isArray(path)) {
for (i = 0; i < path.length; i++) {
this.pick(path[i], obj, true, reindexArray)
}
if (!reindexArray) {
this._cleanup(obj)
}
return obj
} else {
return this.pick(path, obj, true, reindexArray)
}
}
DotObject.prototype._cleanup = function(obj) {
var ret
var i
var keys
var root
if (this.cleanup.length) {
for (i = 0; i < this.cleanup.length; i++) {
keys = this.cleanup[i].split('.')
root = keys.splice(0, -1).join('.')
ret = root ? this.pick(root, obj) : obj
ret = ret[keys[0]].filter(function(v) {
return v !== undefined
})
this.set(this.cleanup[i], ret, obj)
}
this.cleanup = []
}
}
/**
* Alias method for `dot.remove`
*
* Note: this is not an alias for dot.delete()
*
* @param {String|Array<String>} path
* @param {Object} obj
* @param {Boolean} reindexArray
* @return {any} The removed value
*/
DotObject.prototype.del = DotObject.prototype.remove
/**
*
* Move a property from one place to the other.
*
* If the source path does not exist (undefined)
* the target property will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.move = function(source, target, obj, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(target, _process(this.pick(source, obj, true), mods), obj, merge)
} else {
merge = mods
this.set(target, this.pick(source, obj, true), obj, merge)
}
return obj
}
/**
*
* Transfer a property from one object to another object.
*
* If the source path does not exist (undefined)
* the property on the other object will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj1
* @param {Object} obj2
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.transfer = function(
source,
target,
obj1,
obj2,
mods,
merge
) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(
target,
_process(this.pick(source, obj1, true), mods),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, true), obj2, merge)
}
return obj2
}
/**
*
* Copy a property from one object to another object.
*
* If the source path does not exist (undefined)
* the property on the other object will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj1
* @param {Object} obj2
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.copy = function(source, target, obj1, obj2, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(
target,
_process(
// clone what is picked
JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
mods
),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, false), obj2, merge)
}
return obj2
}
/**
*
* Set a property on an object using dot notation.
*
* @param {String} path
* @param {any} val
* @param {Object} obj
* @param {Boolean} merge
*/
DotObject.prototype.set = function(path, val, obj, merge) {
var i
var k
var keys
var key
// Do not operate if the value is undefined.
if (typeof val === 'undefined') {
return obj
}
keys = parsePath(path, this.separator)
for (i = 0; i < keys.length; i++) {
key = keys[i]
if (i === keys.length - 1) {
if (merge && isObject(val) && isObject(obj[key])) {
for (k in val) {
if (hasOwnProperty.call(val, k)) {
obj[key][k] = val[k]
}
}
} else if (merge && Array.isArray(obj[key]) && Array.isArray(val)) {
for (var j = 0; j < val.length; j++) {
obj[keys[i]].push(val[j])
}
} else {
obj[key] = val
}
} else if (
// force the value to be an object
!hasOwnProperty.call(obj, key) ||
(!isObject(obj[key]) && !Array.isArray(obj[key]))
) {
// initialize as array if next key is numeric
if (/^\d+$/.test(keys[i + 1])) {
obj[key] = []
} else {
obj[key] = {}
}
}
obj = obj[key]
}
return obj
}
/**
*
* Transform an object
*
* Usage:
*
* var obj = {
* "id": 1,
* "some": {
* "thing": "else"
* }
* }
*
* var transform = {
* "id": "nr",
* "some.thing": "name"
* }
*
* var tgt = dot.transform(transform, obj)
*
* @param {Object} recipe Transform recipe
* @param {Object} obj Object to be transformed
* @param {Array} mods modifiers for the target
*/
DotObject.prototype.transform = function(recipe, obj, tgt) {
obj = obj || {}
tgt = tgt || {}
Object.keys(recipe).forEach(
function(key) {
this.set(recipe[key], this.pick(key, obj), tgt)
}.bind(this)
)
return tgt
}
/**
*
* Convert object to dotted-key/value pair
*
* Usage:
*
* var tgt = dot.dot(obj)
*
* or
*
* var tgt = {}
* dot.dot(obj, tgt)
*
* @param {Object} obj source object
* @param {Object} tgt target object
* @param {Array} path path array (internal)
*/
DotObject.prototype.dot = function(obj, tgt, path) {
tgt = tgt || {}
path = path || []
var isArray = Array.isArray(obj)
Object.keys(obj).forEach(
function(key) {
var index = isArray && this.useBrackets ? '[' + key + ']' : key
if (
isArrayOrObject(obj[key]) &&
((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
(Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
) {
if (isArray && this.useBrackets) {
var previousKey = path[path.length - 1] || ''
return this.dot(
obj[key],
tgt,
path.slice(0, -1).concat(previousKey + index)
)
} else {
return this.dot(obj[key], tgt, path.concat(index))
}
} else {
if (isArray && this.useBrackets) {
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
} else {
tgt[path.concat(index).join(this.separator)] = obj[key]
}
}
}.bind(this)
)
return tgt
}
DotObject.pick = wrap('pick')
DotObject.move = wrap('move')
DotObject.transfer = wrap('transfer')
DotObject.transform = wrap('transform')
DotObject.copy = wrap('copy')
DotObject.object = wrap('object')
DotObject.str = wrap('str')
DotObject.set = wrap('set')
DotObject.delete = wrap('delete')
DotObject.del = DotObject.remove = wrap('remove')
DotObject.dot = wrap('dot');
['override', 'overwrite'].forEach(function(prop) {
Object.defineProperty(DotObject, prop, {
get: function() {
return dotDefault.override
},
set: function(val) {
dotDefault.override = !!val
}
})
});
['useArray', 'keepArray', 'useBrackets'].forEach(function(prop) {
Object.defineProperty(DotObject, prop, {
get: function() {
return dotDefault[prop]
},
set: function(val) {
dotDefault[prop] = val
}
})
})
DotObject._process = _process
if (typeof define === 'function' && define.amd) {
define(function() {
return DotObject
})
} else if (typeof module != 'undefined' && module.exports) {
module.exports = DotObject
} else {
global[exportName] = DotObject
}
})(this, 'DotObject')

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,59 @@
'use strict'
var gulp = require('gulp')
var gutil = require('gulp-util')
var mocha = require('gulp-mocha')
var hf = require('gulp-headerfooter')
var rename = require('gulp-rename')
var uglify = require('gulp-uglify')
var beautify = require('gulp-beautify')
var eslint = require('gulp-eslint')
var DEST = 'dist/'
var paths = ['gulpfile.js', 'src/dot-object.js', 'test/**/*.js']
gulp.task('lint', function (done) {
gulp.src(paths)
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError())
done()
})
gulp.task('mocha', function (done) {
gulp.src(['test/**/*.js'])
.pipe(mocha())
.on('error', gutil.log)
done()
})
gulp.task('watch', function () {
gulp.watch(paths, gulp.series('build-node', 'mocha'))
})
gulp.task('build-node', function (done) {
gulp.src('src/dot-object.js')
.pipe(hf.footer('\nmodule.exports = DotObject\n'))
.pipe(rename({ basename: 'index' }))
.pipe(gulp.dest('./'))
done()
})
gulp.task('build-bower', function (done) {
gulp.src('src/dot-object.js')
.pipe(hf.header('src/header.tpl'))
.pipe(hf.footer('src/footer.tpl'))
.pipe(beautify({ indentSize: 2 }))
.pipe(gulp.dest(DEST))
.pipe(uglify())
.pipe(rename({ extname: '.min.js' }))
.pipe(gulp.dest(DEST))
done()
})
gulp.task('dist', gulp.parallel('lint', 'build-node', 'mocha', 'build-bower'))
gulp.task('test', gulp.parallel('lint', 'build-node', 'mocha'))
gulp.task('default', gulp.parallel('test'))

View file

@ -0,0 +1,585 @@
'use strict'
function _process (v, mod) {
var i
var r
if (typeof mod === 'function') {
r = mod(v)
if (r !== undefined) {
v = r
}
} else if (Array.isArray(mod)) {
for (i = 0; i < mod.length; i++) {
r = mod[i](v)
if (r !== undefined) {
v = r
}
}
}
return v
}
function parseKey (key, val) {
// detect negative index notation
if (key[0] === '-' && Array.isArray(val) && /^-\d+$/.test(key)) {
return val.length + parseInt(key, 10)
}
return key
}
function isIndex (k) {
return /^\d+$/.test(k)
}
function isObject (val) {
return Object.prototype.toString.call(val) === '[object Object]'
}
function isArrayOrObject (val) {
return Object(val) === val
}
function isEmptyObject (val) {
return Object.keys(val).length === 0
}
var blacklist = ['__proto__', 'prototype', 'constructor']
var blacklistFilter = function (part) { return blacklist.indexOf(part) === -1 }
function parsePath (path, sep) {
if (path.indexOf('[') >= 0) {
path = path.replace(/\[/g, sep).replace(/]/g, '')
}
var parts = path.split(sep)
var check = parts.filter(blacklistFilter)
if (check.length !== parts.length) {
throw Error('Refusing to update blacklisted property ' + path)
}
return parts
}
var hasOwnProperty = Object.prototype.hasOwnProperty
function DotObject (separator, override, useArray, useBrackets) {
if (!(this instanceof DotObject)) {
return new DotObject(separator, override, useArray, useBrackets)
}
if (typeof override === 'undefined') override = false
if (typeof useArray === 'undefined') useArray = true
if (typeof useBrackets === 'undefined') useBrackets = true
this.separator = separator || '.'
this.override = override
this.useArray = useArray
this.useBrackets = useBrackets
this.keepArray = false
// contains touched arrays
this.cleanup = []
}
var dotDefault = new DotObject('.', false, true, true)
function wrap (method) {
return function () {
return dotDefault[method].apply(dotDefault, arguments)
}
}
DotObject.prototype._fill = function (a, obj, v, mod) {
var k = a.shift()
if (a.length > 0) {
obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
if (!isArrayOrObject(obj[k])) {
if (this.override) {
obj[k] = {}
} else {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error(
'Trying to redefine `' + k + '` which is a ' + typeof obj[k]
)
}
return
}
}
this._fill(a, obj[k], v, mod)
} else {
if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error("Trying to redefine non-empty obj['" + k + "']")
}
return
}
obj[k] = _process(v, mod)
}
}
/**
*
* Converts an object with dotted-key/value pairs to it's expanded version
*
* Optionally transformed by a set of modifiers.
*
* Usage:
*
* var row = {
* 'nr': 200,
* 'doc.name': ' My Document '
* }
*
* var mods = {
* 'doc.name': [_s.trim, _s.underscored]
* }
*
* dot.object(row, mods)
*
* @param {Object} obj
* @param {Object} mods
*/
DotObject.prototype.object = function (obj, mods) {
var self = this
Object.keys(obj).forEach(function (k) {
var mod = mods === undefined ? null : mods[k]
// normalize array notation.
var ok = parsePath(k, self.separator).join(self.separator)
if (ok.indexOf(self.separator) !== -1) {
self._fill(ok.split(self.separator), obj, obj[k], mod)
delete obj[k]
} else {
obj[k] = _process(obj[k], mod)
}
})
return obj
}
/**
* @param {String} path dotted path
* @param {String} v value to be set
* @param {Object} obj object to be modified
* @param {Function|Array} mod optional modifier
*/
DotObject.prototype.str = function (path, v, obj, mod) {
var ok = parsePath(path, this.separator).join(this.separator)
if (path.indexOf(this.separator) !== -1) {
this._fill(ok.split(this.separator), obj, v, mod)
} else {
obj[path] = _process(v, mod)
}
return obj
}
/**
*
* Pick a value from an object using dot notation.
*
* Optionally remove the value
*
* @param {String} path
* @param {Object} obj
* @param {Boolean} remove
*/
DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
var i
var keys
var val
var key
var cp
keys = parsePath(path, this.separator)
for (i = 0; i < keys.length; i++) {
key = parseKey(keys[i], obj)
if (obj && typeof obj === 'object' && key in obj) {
if (i === keys.length - 1) {
if (remove) {
val = obj[key]
if (reindexArray && Array.isArray(obj)) {
obj.splice(key, 1)
} else {
delete obj[key]
}
if (Array.isArray(obj)) {
cp = keys.slice(0, -1).join('.')
if (this.cleanup.indexOf(cp) === -1) {
this.cleanup.push(cp)
}
}
return val
} else {
return obj[key]
}
} else {
obj = obj[key]
}
} else {
return undefined
}
}
if (remove && Array.isArray(obj)) {
obj = obj.filter(function (n) {
return n !== undefined
})
}
return obj
}
/**
*
* Delete value from an object using dot notation.
*
* @param {String} path
* @param {Object} obj
* @return {any} The removed value
*/
DotObject.prototype.delete = function (path, obj) {
return this.remove(path, obj, true)
}
/**
*
* Remove value from an object using dot notation.
*
* Will remove multiple items if path is an array.
* In this case array indexes will be retained until all
* removals have been processed.
*
* Use dot.delete() to automatically re-index arrays.
*
* @param {String|Array<String>} path
* @param {Object} obj
* @param {Boolean} reindexArray
* @return {any} The removed value
*/
DotObject.prototype.remove = function (path, obj, reindexArray) {
var i
this.cleanup = []
if (Array.isArray(path)) {
for (i = 0; i < path.length; i++) {
this.pick(path[i], obj, true, reindexArray)
}
if (!reindexArray) {
this._cleanup(obj)
}
return obj
} else {
return this.pick(path, obj, true, reindexArray)
}
}
DotObject.prototype._cleanup = function (obj) {
var ret
var i
var keys
var root
if (this.cleanup.length) {
for (i = 0; i < this.cleanup.length; i++) {
keys = this.cleanup[i].split('.')
root = keys.splice(0, -1).join('.')
ret = root ? this.pick(root, obj) : obj
ret = ret[keys[0]].filter(function (v) {
return v !== undefined
})
this.set(this.cleanup[i], ret, obj)
}
this.cleanup = []
}
}
/**
* Alias method for `dot.remove`
*
* Note: this is not an alias for dot.delete()
*
* @param {String|Array<String>} path
* @param {Object} obj
* @param {Boolean} reindexArray
* @return {any} The removed value
*/
DotObject.prototype.del = DotObject.prototype.remove
/**
*
* Move a property from one place to the other.
*
* If the source path does not exist (undefined)
* the target property will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.move = function (source, target, obj, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(target, _process(this.pick(source, obj, true), mods), obj, merge)
} else {
merge = mods
this.set(target, this.pick(source, obj, true), obj, merge)
}
return obj
}
/**
*
* Transfer a property from one object to another object.
*
* If the source path does not exist (undefined)
* the property on the other object will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj1
* @param {Object} obj2
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.transfer = function (
source,
target,
obj1,
obj2,
mods,
merge
) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(
target,
_process(this.pick(source, obj1, true), mods),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, true), obj2, merge)
}
return obj2
}
/**
*
* Copy a property from one object to another object.
*
* If the source path does not exist (undefined)
* the property on the other object will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj1
* @param {Object} obj2
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(
target,
_process(
// clone what is picked
JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
mods
),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, false), obj2, merge)
}
return obj2
}
/**
*
* Set a property on an object using dot notation.
*
* @param {String} path
* @param {any} val
* @param {Object} obj
* @param {Boolean} merge
*/
DotObject.prototype.set = function (path, val, obj, merge) {
var i
var k
var keys
var key
// Do not operate if the value is undefined.
if (typeof val === 'undefined') {
return obj
}
keys = parsePath(path, this.separator)
for (i = 0; i < keys.length; i++) {
key = keys[i]
if (i === keys.length - 1) {
if (merge && isObject(val) && isObject(obj[key])) {
for (k in val) {
if (hasOwnProperty.call(val, k)) {
obj[key][k] = val[k]
}
}
} else if (merge && Array.isArray(obj[key]) && Array.isArray(val)) {
for (var j = 0; j < val.length; j++) {
obj[keys[i]].push(val[j])
}
} else {
obj[key] = val
}
} else if (
// force the value to be an object
!hasOwnProperty.call(obj, key) ||
(!isObject(obj[key]) && !Array.isArray(obj[key]))
) {
// initialize as array if next key is numeric
if (/^\d+$/.test(keys[i + 1])) {
obj[key] = []
} else {
obj[key] = {}
}
}
obj = obj[key]
}
return obj
}
/**
*
* Transform an object
*
* Usage:
*
* var obj = {
* "id": 1,
* "some": {
* "thing": "else"
* }
* }
*
* var transform = {
* "id": "nr",
* "some.thing": "name"
* }
*
* var tgt = dot.transform(transform, obj)
*
* @param {Object} recipe Transform recipe
* @param {Object} obj Object to be transformed
* @param {Array} mods modifiers for the target
*/
DotObject.prototype.transform = function (recipe, obj, tgt) {
obj = obj || {}
tgt = tgt || {}
Object.keys(recipe).forEach(
function (key) {
this.set(recipe[key], this.pick(key, obj), tgt)
}.bind(this)
)
return tgt
}
/**
*
* Convert object to dotted-key/value pair
*
* Usage:
*
* var tgt = dot.dot(obj)
*
* or
*
* var tgt = {}
* dot.dot(obj, tgt)
*
* @param {Object} obj source object
* @param {Object} tgt target object
* @param {Array} path path array (internal)
*/
DotObject.prototype.dot = function (obj, tgt, path) {
tgt = tgt || {}
path = path || []
var isArray = Array.isArray(obj)
Object.keys(obj).forEach(
function (key) {
var index = isArray && this.useBrackets ? '[' + key + ']' : key
if (
isArrayOrObject(obj[key]) &&
((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
(Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
) {
if (isArray && this.useBrackets) {
var previousKey = path[path.length - 1] || ''
return this.dot(
obj[key],
tgt,
path.slice(0, -1).concat(previousKey + index)
)
} else {
return this.dot(obj[key], tgt, path.concat(index))
}
} else {
if (isArray && this.useBrackets) {
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
} else {
tgt[path.concat(index).join(this.separator)] = obj[key]
}
}
}.bind(this)
)
return tgt
}
DotObject.pick = wrap('pick')
DotObject.move = wrap('move')
DotObject.transfer = wrap('transfer')
DotObject.transform = wrap('transform')
DotObject.copy = wrap('copy')
DotObject.object = wrap('object')
DotObject.str = wrap('str')
DotObject.set = wrap('set')
DotObject.delete = wrap('delete')
DotObject.del = DotObject.remove = wrap('remove')
DotObject.dot = wrap('dot');
['override', 'overwrite'].forEach(function (prop) {
Object.defineProperty(DotObject, prop, {
get: function () {
return dotDefault.override
},
set: function (val) {
dotDefault.override = !!val
}
})
});
['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
Object.defineProperty(DotObject, prop, {
get: function () {
return dotDefault[prop]
},
set: function (val) {
dotDefault[prop] = val
}
})
})
DotObject._process = _process
module.exports = DotObject

View file

@ -0,0 +1,61 @@
{
"name": "dot-object",
"description": "dot-object makes it possible to transform and read (JSON) objects using dot notation.",
"version": "2.1.4",
"author": {
"name": "Rob Halff",
"email": "rob.halff@gmail.com"
},
"repository": {
"type": "git",
"url": "git://github.com/rhalff/dot-object.git"
},
"bugs": {
"url": "https://github.com/rhalff/dot-object/issues"
},
"main": "index",
"bin": "./bin/dot-object",
"scripts": {
"test": "gulp test",
"watch": "gulp watch",
"lint": "gulp lint",
"dist": "gulp dist"
},
"standard": {
"globals": [
"it",
"describe",
"beforeEach"
]
},
"devDependencies": {
"eslint": "^6.6.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"gulp": "^4.0.2",
"gulp-beautify": "^3.0.0",
"gulp-eslint": "^6.0.0",
"gulp-headerfooter": "^1.0.3",
"gulp-mocha": "^7.0.2",
"gulp-rename": "^1.4.0",
"gulp-uglify": "^3.0.2",
"gulp-util": "^3.0.8",
"mocha": "6.x.x",
"should": "13.x.x",
"underscore.string": "latest"
},
"keywords": [
"json",
"filter",
"transform",
"dot notation",
"dot"
],
"dependencies": {
"commander": "^4.0.0",
"glob": "^7.1.5"
}
}

View file

@ -0,0 +1,583 @@
'use strict'
function _process (v, mod) {
var i
var r
if (typeof mod === 'function') {
r = mod(v)
if (r !== undefined) {
v = r
}
} else if (Array.isArray(mod)) {
for (i = 0; i < mod.length; i++) {
r = mod[i](v)
if (r !== undefined) {
v = r
}
}
}
return v
}
function parseKey (key, val) {
// detect negative index notation
if (key[0] === '-' && Array.isArray(val) && /^-\d+$/.test(key)) {
return val.length + parseInt(key, 10)
}
return key
}
function isIndex (k) {
return /^\d+$/.test(k)
}
function isObject (val) {
return Object.prototype.toString.call(val) === '[object Object]'
}
function isArrayOrObject (val) {
return Object(val) === val
}
function isEmptyObject (val) {
return Object.keys(val).length === 0
}
var blacklist = ['__proto__', 'prototype', 'constructor']
var blacklistFilter = function (part) { return blacklist.indexOf(part) === -1 }
function parsePath (path, sep) {
if (path.indexOf('[') >= 0) {
path = path.replace(/\[/g, sep).replace(/]/g, '')
}
var parts = path.split(sep)
var check = parts.filter(blacklistFilter)
if (check.length !== parts.length) {
throw Error('Refusing to update blacklisted property ' + path)
}
return parts
}
var hasOwnProperty = Object.prototype.hasOwnProperty
function DotObject (separator, override, useArray, useBrackets) {
if (!(this instanceof DotObject)) {
return new DotObject(separator, override, useArray, useBrackets)
}
if (typeof override === 'undefined') override = false
if (typeof useArray === 'undefined') useArray = true
if (typeof useBrackets === 'undefined') useBrackets = true
this.separator = separator || '.'
this.override = override
this.useArray = useArray
this.useBrackets = useBrackets
this.keepArray = false
// contains touched arrays
this.cleanup = []
}
var dotDefault = new DotObject('.', false, true, true)
function wrap (method) {
return function () {
return dotDefault[method].apply(dotDefault, arguments)
}
}
DotObject.prototype._fill = function (a, obj, v, mod) {
var k = a.shift()
if (a.length > 0) {
obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
if (!isArrayOrObject(obj[k])) {
if (this.override) {
obj[k] = {}
} else {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error(
'Trying to redefine `' + k + '` which is a ' + typeof obj[k]
)
}
return
}
}
this._fill(a, obj[k], v, mod)
} else {
if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
if (!(isArrayOrObject(v) && isEmptyObject(v))) {
throw new Error("Trying to redefine non-empty obj['" + k + "']")
}
return
}
obj[k] = _process(v, mod)
}
}
/**
*
* Converts an object with dotted-key/value pairs to it's expanded version
*
* Optionally transformed by a set of modifiers.
*
* Usage:
*
* var row = {
* 'nr': 200,
* 'doc.name': ' My Document '
* }
*
* var mods = {
* 'doc.name': [_s.trim, _s.underscored]
* }
*
* dot.object(row, mods)
*
* @param {Object} obj
* @param {Object} mods
*/
DotObject.prototype.object = function (obj, mods) {
var self = this
Object.keys(obj).forEach(function (k) {
var mod = mods === undefined ? null : mods[k]
// normalize array notation.
var ok = parsePath(k, self.separator).join(self.separator)
if (ok.indexOf(self.separator) !== -1) {
self._fill(ok.split(self.separator), obj, obj[k], mod)
delete obj[k]
} else {
obj[k] = _process(obj[k], mod)
}
})
return obj
}
/**
* @param {String} path dotted path
* @param {String} v value to be set
* @param {Object} obj object to be modified
* @param {Function|Array} mod optional modifier
*/
DotObject.prototype.str = function (path, v, obj, mod) {
var ok = parsePath(path, this.separator).join(this.separator)
if (path.indexOf(this.separator) !== -1) {
this._fill(ok.split(this.separator), obj, v, mod)
} else {
obj[path] = _process(v, mod)
}
return obj
}
/**
*
* Pick a value from an object using dot notation.
*
* Optionally remove the value
*
* @param {String} path
* @param {Object} obj
* @param {Boolean} remove
*/
DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
var i
var keys
var val
var key
var cp
keys = parsePath(path, this.separator)
for (i = 0; i < keys.length; i++) {
key = parseKey(keys[i], obj)
if (obj && typeof obj === 'object' && key in obj) {
if (i === keys.length - 1) {
if (remove) {
val = obj[key]
if (reindexArray && Array.isArray(obj)) {
obj.splice(key, 1)
} else {
delete obj[key]
}
if (Array.isArray(obj)) {
cp = keys.slice(0, -1).join('.')
if (this.cleanup.indexOf(cp) === -1) {
this.cleanup.push(cp)
}
}
return val
} else {
return obj[key]
}
} else {
obj = obj[key]
}
} else {
return undefined
}
}
if (remove && Array.isArray(obj)) {
obj = obj.filter(function (n) {
return n !== undefined
})
}
return obj
}
/**
*
* Delete value from an object using dot notation.
*
* @param {String} path
* @param {Object} obj
* @return {any} The removed value
*/
DotObject.prototype.delete = function (path, obj) {
return this.remove(path, obj, true)
}
/**
*
* Remove value from an object using dot notation.
*
* Will remove multiple items if path is an array.
* In this case array indexes will be retained until all
* removals have been processed.
*
* Use dot.delete() to automatically re-index arrays.
*
* @param {String|Array<String>} path
* @param {Object} obj
* @param {Boolean} reindexArray
* @return {any} The removed value
*/
DotObject.prototype.remove = function (path, obj, reindexArray) {
var i
this.cleanup = []
if (Array.isArray(path)) {
for (i = 0; i < path.length; i++) {
this.pick(path[i], obj, true, reindexArray)
}
if (!reindexArray) {
this._cleanup(obj)
}
return obj
} else {
return this.pick(path, obj, true, reindexArray)
}
}
DotObject.prototype._cleanup = function (obj) {
var ret
var i
var keys
var root
if (this.cleanup.length) {
for (i = 0; i < this.cleanup.length; i++) {
keys = this.cleanup[i].split('.')
root = keys.splice(0, -1).join('.')
ret = root ? this.pick(root, obj) : obj
ret = ret[keys[0]].filter(function (v) {
return v !== undefined
})
this.set(this.cleanup[i], ret, obj)
}
this.cleanup = []
}
}
/**
* Alias method for `dot.remove`
*
* Note: this is not an alias for dot.delete()
*
* @param {String|Array<String>} path
* @param {Object} obj
* @param {Boolean} reindexArray
* @return {any} The removed value
*/
DotObject.prototype.del = DotObject.prototype.remove
/**
*
* Move a property from one place to the other.
*
* If the source path does not exist (undefined)
* the target property will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.move = function (source, target, obj, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(target, _process(this.pick(source, obj, true), mods), obj, merge)
} else {
merge = mods
this.set(target, this.pick(source, obj, true), obj, merge)
}
return obj
}
/**
*
* Transfer a property from one object to another object.
*
* If the source path does not exist (undefined)
* the property on the other object will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj1
* @param {Object} obj2
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.transfer = function (
source,
target,
obj1,
obj2,
mods,
merge
) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(
target,
_process(this.pick(source, obj1, true), mods),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, true), obj2, merge)
}
return obj2
}
/**
*
* Copy a property from one object to another object.
*
* If the source path does not exist (undefined)
* the property on the other object will not be set.
*
* @param {String} source
* @param {String} target
* @param {Object} obj1
* @param {Object} obj2
* @param {Function|Array} mods
* @param {Boolean} merge
*/
DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) {
if (typeof mods === 'function' || Array.isArray(mods)) {
this.set(
target,
_process(
// clone what is picked
JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
mods
),
obj2,
merge
)
} else {
merge = mods
this.set(target, this.pick(source, obj1, false), obj2, merge)
}
return obj2
}
/**
*
* Set a property on an object using dot notation.
*
* @param {String} path
* @param {any} val
* @param {Object} obj
* @param {Boolean} merge
*/
DotObject.prototype.set = function (path, val, obj, merge) {
var i
var k
var keys
var key
// Do not operate if the value is undefined.
if (typeof val === 'undefined') {
return obj
}
keys = parsePath(path, this.separator)
for (i = 0; i < keys.length; i++) {
key = keys[i]
if (i === keys.length - 1) {
if (merge && isObject(val) && isObject(obj[key])) {
for (k in val) {
if (hasOwnProperty.call(val, k)) {
obj[key][k] = val[k]
}
}
} else if (merge && Array.isArray(obj[key]) && Array.isArray(val)) {
for (var j = 0; j < val.length; j++) {
obj[keys[i]].push(val[j])
}
} else {
obj[key] = val
}
} else if (
// force the value to be an object
!hasOwnProperty.call(obj, key) ||
(!isObject(obj[key]) && !Array.isArray(obj[key]))
) {
// initialize as array if next key is numeric
if (/^\d+$/.test(keys[i + 1])) {
obj[key] = []
} else {
obj[key] = {}
}
}
obj = obj[key]
}
return obj
}
/**
*
* Transform an object
*
* Usage:
*
* var obj = {
* "id": 1,
* "some": {
* "thing": "else"
* }
* }
*
* var transform = {
* "id": "nr",
* "some.thing": "name"
* }
*
* var tgt = dot.transform(transform, obj)
*
* @param {Object} recipe Transform recipe
* @param {Object} obj Object to be transformed
* @param {Array} mods modifiers for the target
*/
DotObject.prototype.transform = function (recipe, obj, tgt) {
obj = obj || {}
tgt = tgt || {}
Object.keys(recipe).forEach(
function (key) {
this.set(recipe[key], this.pick(key, obj), tgt)
}.bind(this)
)
return tgt
}
/**
*
* Convert object to dotted-key/value pair
*
* Usage:
*
* var tgt = dot.dot(obj)
*
* or
*
* var tgt = {}
* dot.dot(obj, tgt)
*
* @param {Object} obj source object
* @param {Object} tgt target object
* @param {Array} path path array (internal)
*/
DotObject.prototype.dot = function (obj, tgt, path) {
tgt = tgt || {}
path = path || []
var isArray = Array.isArray(obj)
Object.keys(obj).forEach(
function (key) {
var index = isArray && this.useBrackets ? '[' + key + ']' : key
if (
isArrayOrObject(obj[key]) &&
((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
(Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
) {
if (isArray && this.useBrackets) {
var previousKey = path[path.length - 1] || ''
return this.dot(
obj[key],
tgt,
path.slice(0, -1).concat(previousKey + index)
)
} else {
return this.dot(obj[key], tgt, path.concat(index))
}
} else {
if (isArray && this.useBrackets) {
tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
} else {
tgt[path.concat(index).join(this.separator)] = obj[key]
}
}
}.bind(this)
)
return tgt
}
DotObject.pick = wrap('pick')
DotObject.move = wrap('move')
DotObject.transfer = wrap('transfer')
DotObject.transform = wrap('transform')
DotObject.copy = wrap('copy')
DotObject.object = wrap('object')
DotObject.str = wrap('str')
DotObject.set = wrap('set')
DotObject.delete = wrap('delete')
DotObject.del = DotObject.remove = wrap('remove')
DotObject.dot = wrap('dot');
['override', 'overwrite'].forEach(function (prop) {
Object.defineProperty(DotObject, prop, {
get: function () {
return dotDefault.override
},
set: function (val) {
dotDefault.override = !!val
}
})
});
['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
Object.defineProperty(DotObject, prop, {
get: function () {
return dotDefault[prop]
},
set: function (val) {
dotDefault[prop] = val
}
})
})
DotObject._process = _process

View file

@ -0,0 +1,13 @@
if (typeof define === 'function' && define.amd) {
define(function() {
return DotObject
})
} else if (typeof module != 'undefined' && module.exports) {
module.exports = DotObject
} else {
global[exportName] = DotObject
}
})(this, 'DotObject')

View file

@ -0,0 +1 @@
(function(global, exportName) {

View file

@ -0,0 +1,125 @@
'use strict'
require('should')
var Dot = require('../index')
describe('_process:', function () {
describe('Should process modifier', function () {
describe('if value is a string', function () {
function up (val) {
return val.toUpperCase()
}
it('using a single modifier', function () {
Dot._process('k', up).should.eql('K')
})
it('using an array of modifiers', function () {
var v = 'k'
Dot._process(v, [up]).should.eql('K')
})
})
describe('if value is an object', function () {
function withReturn (val) {
val.withReturn = 'return'
return val
}
function noReturn (val) {
val.noReturn = 'no return'
}
it('using a single modifier *with* return', function () {
var a = {
test: 1
}
var expected = {
test: 1,
withReturn: 'return'
}
var ret = Dot._process(a, withReturn)
a.should.eql(expected)
ret.should.eql(expected)
})
it('using a single modifier *without* return', function () {
var a = {
test: 1
}
var expected = {
test: 1,
noReturn: 'no return'
}
var ret = Dot._process(a, noReturn)
a.should.eql(expected)
ret.should.eql(expected)
})
it('using an array of modifiers *with* return and *without* return',
function () {
var a = {
test: 1
}
var expected = {
test: 1,
withReturn: 'return',
noReturn: 'no return'
}
var ret = Dot._process(a, [withReturn, noReturn])
a.should.eql(expected)
ret.should.eql(expected)
}
)
})
describe('if value is an array', function () {
function withReturn (val) {
val.push('return')
return val
}
function noReturn (val) {
val.push('no return')
}
it('using a single modifier *with* return', function () {
var a = [1]
var expected = [1, 'return']
var ret = Dot._process(a, withReturn)
a.should.eql(expected)
ret.should.eql(expected)
})
it('using a single modifier *without* return', function () {
var a = [1]
var expected = [1, 'no return']
var ret = Dot._process(a, noReturn)
a.should.eql(expected)
ret.should.eql(expected)
})
it('using an array of modifiers *with* return and *without* return',
function () {
var a = [1]
var expected = [1, 'return', 'no return']
var ret = Dot._process(a, [withReturn, noReturn])
a.should.eql(expected)
ret.should.eql(expected)
}
)
})
})
})

View file

@ -0,0 +1,176 @@
'use strict'
/* jshint -W030 */
require('should')
var Dot = require('../index')
describe('Dotted Array notation', function () {
var src
beforeEach(function () {
src = {
path: [{
longitude: 5.512482166290283,
latitude: 52.5006217956543
}, {
longitude: 5.512370586395264,
latitude: 52.50059509277344
}, {
longitude: 5.512370586395264,
latitude: 52.50059509277344
}]
}
})
function runVariant (type) {
var v = function (v) {
if (type === 'bracket') {
// rewrite some.prop.1 to some.prop[1]
return v.replace(/\.(-?\d+)/g, '[$1]')
} else {
return v
}
}
describe('can pick', function () {
it('index', function () {
Dot.pick(v('path.0'), src).should.eql(src.path[0])
Dot.pick(v('path.2'), src).should.eql(src.path[2])
;(typeof Dot.pick(v('path.9'), src)).should.eql('undefined')
})
it('negative index', function () {
Dot.pick(v('path.-1'), src).should.eql(src.path[2])
Dot.pick(v('path.-2'), src).should.eql(src.path[1])
Dot.pick(v('path.-3'), src).should.eql(src.path[0])
;(typeof Dot.pick(v('path.-9'), src)).should.eql('undefined')
})
it('non-array `-` prefixed properties', function () {
var src = {
path: {
'-1': 'test1',
'-2': 'test2',
'-3': 'test3',
'----key': 'test4'
}
}
Dot.pick(v('path.-1'), src).should.eql('test1')
Dot.pick(v('path.-2'), src).should.eql('test2')
Dot.pick(v('path.-3'), src).should.eql('test3')
Dot.pick(v('path.----key'), src).should.eql('test4')
;(typeof Dot.pick(v('path.-9'), src)).should.eql('undefined')
})
it('multiple indexes', function () {
var src = {
I: [
{ am: [{ nes: ['ted'] }] },
{ me: 'too' }
]
}
Dot.pick(v('I.0'), src).should.eql(src.I[0])
Dot.pick(v('I.0.am'), src).should.eql(src.I[0].am)
Dot.pick(v('I.0.am.0'), src).should.eql(src.I[0].am[0])
Dot.pick(v('I.0.am.0.nes'), src).should.eql(src.I[0].am[0].nes)
Dot.pick(v('I.0.am.0.nes.0'), src).should.eql('ted')
Dot.pick(v('I.1.me'), src).should.eql('too')
})
})
describe('can set', function () {
it('index at target', function () {
var obj = { path: [] }
Dot.set(v('path.0'), 'test', obj)
Dot.set(v('path.1'), 'test2', obj)
obj.path.should.be.instanceOf(Array)
obj.should.eql({ path: ['test', 'test2'] })
})
it('index and set undefined for empty indices', function () {
var obj = { path: [] }
Dot.set(v('path.0'), 'test', obj)
Dot.set(v('path.2'), 'test2', obj)
obj.path.should.be.instanceOf(Array)
// array will have an undefined index.
JSON.stringify(obj)
.should.eql(
JSON.stringify({ path: ['test', undefined, 'test2'] })
)
// to json will converted it to null
JSON.stringify(obj).should.eql('{"path":["test",null,"test2"]}')
})
it('index and overwrite existing values', function () {
var obj = { path: ['still', 'shall', 'be', 'gone', 'here'] }
Dot.set(v('path.1'), 'x', obj)
Dot.set(v('path.2'), 'xx', obj)
Dot.set(v('path.3'), 'xxx', obj)
obj.should.eql({ path: ['still', 'x', 'xx', 'xxx', 'here'] })
})
})
describe('can remove', function () {
it('indexes one by one leaving traces', function () {
var obj = { path: ['still', 'shall', 'really', 'be', 'gone', 'here'] }
Dot.remove(v('path.1'), obj)
Dot.remove(v('path.2'), obj)
Dot.del(v('path.3'), obj) // use alias
Dot.del(v('path.4'), obj)
// array will have an undefined index.
JSON.stringify(obj)
.should.eql(
JSON.stringify({
path: [
'still', undefined, undefined, undefined, undefined, 'here'
]
})
)
// to json will converted it to null
JSON.stringify(obj).should.eql(
'{"path":["still",null,null,null,null,"here"]}'
)
})
it('array of indexes leaving no traces', function () {
var obj = { path: ['still', 'shall', 'really', 'be', 'gone', 'here'] }
Dot.remove([
v('path.1'),
v('path.2'),
v('path.3'),
v('path.4')], obj)
JSON.stringify(obj).should.eql('{"path":["still","here"]}')
})
})
}
describe('with dot notation', function () {
runVariant()
})
// extra logic no real benefit.
describe('with bracket notation', function () {
runVariant('bracket')
})
describe('Refuse to update __proto__', function () {
var obj = { path: [] }
;(() => Dot.set('path[0].__proto__.toString', 'test', obj)).should.throw(/Refusing to update/)
})
})

View file

@ -0,0 +1,86 @@
'use strict'
require('should')
var Dot = require('../index')
describe('Copy:', function () {
it('Should be able to copy properties', function () {
var src = {
name: 'John',
stuff: {
phone: {
brand: 'iphone',
version: 6
}
}
}
var tgt = {
name: 'Brandon'
}
var srcExpected = JSON.parse(JSON.stringify(src))
var tgtExpected = {
name: 'Brandon',
copied: 'John',
wanna: {
haves: {
phone: {
brand: 'iphone',
version: 6
}
}
}
}
// copy object
Dot.copy('stuff.phone', 'wanna.haves.phone', src, tgt)
// copy string
Dot.copy('name', 'copied', src, tgt)
src.should.eql(srcExpected)
tgt.should.eql(tgtExpected)
})
it('Should process modifiers', function () {
function up (val) {
val.brand = val.brand.toUpperCase()
return val
}
var src = {
name: 'John',
stuff: {
phone: {
brand: 'iphone',
version: 6
}
}
}
var tgt = {
name: 'Brandon'
}
var srcExpected = JSON.parse(JSON.stringify(src))
var tgtExpected = {
name: 'Brandon',
wanna: {
haves: {
phone: {
brand: 'IPHONE',
version: 6
}
}
}
}
Dot.copy('stuff.phone', 'wanna.haves.phone', src, tgt, up)
src.should.eql(srcExpected)
tgt.should.eql(tgtExpected)
})
})

View file

@ -0,0 +1,30 @@
{
"name": "array deep bug #10",
"options": {
"useArray": true
},
"input": {
"a[0]": "A0",
"a[1]": "A1",
"a[2]": "A2",
"a[3].b[0].c[0]": "A3B0C0",
"a[3].b[0].c[1]": "A3B0C1"
},
"expected": {
"a": [
"A0",
"A1",
"A2",
{
"b": [
{
"c": [
"A3B0C0",
"A3B0C1"
]
}
]
}
]
}
}

View file

@ -0,0 +1,19 @@
{
"name": "array deep bug2 #10",
"options": {
"useArray": true
},
"input": {
"b.0.c.1": "b0c1",
"b.0.c.2": "b0c2"
},
"expected": {
"b" : [ {
"c" : [
null,
"b0c1",
"b0c2"
]
}]
}
}

View file

@ -0,0 +1,43 @@
{
"name": "empty array",
"input": [
{
"a": []
},
{
"a.0": 1,
"a": []
},
{
"a": [],
"a.0": 2
},
{
"a.0.b": []
},
{
"b.a.0": "b",
"b.a": [],
"b.a.1": "c"
}
],
"expected": [
{
"a": []
},
{
"a": [1]
},
{
"a": [2]
},
{
"a" : [ {"b" : [] } ]
},
{
"b" : {
"a": [ "b", "c" ]
}
}
]
}

View file

@ -0,0 +1,23 @@
{
"name": "empty object",
"input": [
{
"a.b": {}
},
{
"a.b.c": 1,
"a.b": {} ,
"a.b.d": 2
}
],
"expected": [
{
"a" : {"b" : {} }
},
{
"a" : {
"b": { "c": 1, "d": 2 }
}
}
]
}

View file

@ -0,0 +1,8 @@
module.exports = [
require('./array_deep_bug'),
require('./array_deep_bug2'),
require('./empty_array'),
require('./empty_object'),
require('./object_deep_numeric_keys'),
require('./object_deep_numeric_keys2')
]

View file

@ -0,0 +1,30 @@
{
"name": "object deep numeric keys",
"options": {
"useArray": false
},
"input": {
"a[0]": "A0",
"a[1]": "A1",
"a[2]": "A2",
"a[3].b[0].c[0]": "A3B0C0",
"a[3].b[0].c[1]": "A3B0C1"
},
"expected": {
"a": {
"0": "A0",
"1": "A1",
"2": "A2",
"3": {
"b": {
"0": {
"c": {
"0": "A3B0C0",
"1": "A3B0C1"
}
}
}
}
}
}
}

View file

@ -0,0 +1,20 @@
{
"name": "object deep numeric keys 2 #10",
"options": {
"useArray": false
},
"input": {
"b.0.c.1": "b0c1",
"b.0.c.2": "b0c2"
},
"expected": {
"b": {
"0": {
"c": {
"1": "b0c1",
"2": "b0c2"
}
}
}
}
}

View file

@ -0,0 +1,237 @@
'use strict'
require('should')
var _s = require('underscore.string')
var Dot = require('../index')
describe('Object test:', function () {
it('Should expand dotted keys', function () {
var row = {
id: 2,
'contact.name.first': 'John',
'contact.name.last': 'Doe',
'contact.email': 'example@gmail.com',
'contact.info.about.me': 'classified'
}
Dot.object(row)
row.should.eql({
id: 2,
contact: {
name: {
first: 'John',
last: 'Doe'
},
email: 'example@gmail.com',
info: {
about: {
me: 'classified'
}
}
}
})
})
it('Should expand dotted keys with array notation', function () {
var row = {
id: 2,
'my.arr.0': 'one',
'my.arr.1': 'two',
'my.arr.2': 'three',
'my.arr2[0]': 'one',
'my.arr2[1]': 'two',
'my.arr2[2]': 'three'
}
Dot.object(row)
row.should.eql({
id: 2,
my: {
arr: ['one', 'two', 'three'],
arr2: ['one', 'two', 'three']
}
})
})
it('Should expand dotted keys with array notation with different separator', function () {
var row = {
id: 2,
my_arr_0: 'one',
my_arr_1: 'two',
my_arr_2: 'three',
'my_arr2[0]': 'one',
'my_arr2[1]': 'two',
'my_arr2[2]': 'three'
}
new Dot('_').object(row)
row.should.eql({
id: 2,
my: {
arr: ['one', 'two', 'three'],
arr2: ['one', 'two', 'three']
}
})
})
it('Should allow keys with numbers', function () {
var row = {
id: 2,
'0A': 'a',
'0A9': 'b',
'0B.1AB.A34C9': 'c'
}
Dot.object(row)
row.should.eql({
id: 2,
'0A': 'a',
'0A9': 'b',
'0B': {
'1AB': {
A34C9: 'c'
}
}
})
})
it('Should expand dotted string', function () {
var tgt = {}
Dot.str('this.is.my.string', 'value', tgt)
tgt.should.eql({
this: {
is: {
my: {
string: 'value'
}
}
}
})
})
it('Dot.str Redefinition should fail', function () {
var tgt = {
already: 'set'
}
;(function () {
Dot.str('already.new', 'value', tgt)
}).should.throw('Trying to redefine `already` which is a string')
})
it('Dot.str should process a modifier', function () {
var tgt = {}
Dot.str('this.is.my.string', 'value', tgt, _s.capitalize)
tgt.should.eql({
this: {
is: {
my: {
string: 'Value'
}
}
}
})
})
it('Dot.str should process multiple modifiers', function () {
var tgt = {}
Dot.str(
'this.is.my.string',
' this is a test ',
tgt, [_s.trim, _s.underscored]
)
tgt.should.eql({
this: {
is: {
my: {
string: 'this_is_a_test'
}
}
}
})
})
it('Dot.object should process a modifier', function () {
var row = {
'page.title': 'my page',
'page.slug': 'My Page'
}
var mods = {
'page.title': _s.titleize,
'page.slug': _s.slugify
}
Dot.object(row, mods)
row.should.eql({ page: { title: 'My Page', slug: 'my-page' } })
})
it('should process root properties',
function () {
var row = {
nr: 200,
'nested.nr': 200
}
var mods = {
nr: [val => val * 2],
'nested.nr': [val => val * 2]
}
Dot.object(row, mods)
row.should.eql({ nr: 400, nested: { nr: 400 } })
}
)
it('should process non dot value with modifier when override is false',
function () {
var row = { title: 'my page', slug: 'My Page' }
var mods = { title: _s.titleize, slug: _s.slugify }
Dot.object(row, mods)
row.should.eql({ title: 'My Page', slug: 'my-page' })
}
)
it('Dot.object should process multiple modifiers', function () {
var row = { 'page.name': ' My Page ' }
var mods = { 'page.name': [_s.trim, _s.underscored] }
Dot.object(row, mods)
row.should.eql({ page: { name: 'my_page' } })
})
it('Dot.object should work with a different separator', function () {
var row = { 'page=>name': ' My Page ' }
var mods = { 'page=>name': [_s.trim, _s.underscored] }
var dot = new Dot('=>', false)
dot.object(row, mods)
row.should.eql({ page: { name: 'my_page' } })
})
it('Dot.object should disallow to set __proto__', function () {
var row = { '__proto__.toString': 'hi' }
var dot = new Dot()
;(() => dot.object(row)).should.throw(/Refusing to update/)
})
})

View file

@ -0,0 +1,155 @@
'use strict'
require('should')
var Dot = require('../index')
var pkg = require('./fixtures/package.json')
describe('dot():', function () {
var obj
// Dot.useBrackets = false;
beforeEach(function () {
obj = {
id: 'my-id',
nes: {
ted: {
value: true
}
},
other: {
nested: {
stuff: 5
}
},
nested: {
array: [
{
with: 'object1'
},
{
and: 'object2'
}
]
},
some: {
array: ['A', 'B']
},
ehrm: 123,
dates: {
first: new Date('Mon Oct 13 2014 00:00:00 GMT+0100 (BST)')
},
arrays: [
[
[
{
all: [
[
{
the: [
'way',
['down']
]
}
]
]
}
]
]
]
}
})
it('Should be able to convert to dotted-key/value pairs', function () {
var expected = {
id: 'my-id',
'nes.ted.value': true,
'other.nested.stuff': 5,
'nested.array[0].with': 'object1',
'nested.array[1].and': 'object2',
'some.array[0]': 'A',
'some.array[1]': 'B',
ehrm: 123,
'dates.first': new Date('Mon Oct 13 2014 00:00:00 GMT+0100 (BST)'),
'arrays[0][0][0].all[0][0].the[0]': 'way',
'arrays[0][0][0].all[0][0].the[1][0]': 'down'
}
Dot.dot(obj).should.eql(expected)
})
it('dot() should equal object()', function () {
Dot.object(Dot.dot(pkg)).should.eql(pkg)
})
it('keepArray prevents arrays from being dotted', function () {
var expected = {
id: 'my-id',
'nes.ted.value': true,
'other.nested.stuff': 5,
'nested.array': [{
with: 'object1'
}, {
and: 'object2'
}],
'some.array': ['A', 'B'],
ehrm: 123,
'dates.first': new Date('Mon Oct 13 2014 00:00:00 GMT+0100 (BST)'),
arrays: JSON.parse(JSON.stringify(obj.arrays))
}
Dot.keepArray = true
Dot.dot(obj).should.eql(expected)
Dot.keepArray = false
})
it('useBrackets wrap indexes with brackets', function () {
var expected = {
id: 'my-id',
'nes.ted.value': true,
'other.nested.stuff': 5,
'nested.array[0].with': 'object1',
'nested.array[1].and': 'object2',
'some.array[0]': 'A',
'some.array[1]': 'B',
ehrm: 123,
'dates.first': new Date('Mon Oct 13 2014 00:00:00 GMT+0100 (BST)'),
'arrays[0][0][0].all[0][0].the[0]': 'way',
'arrays[0][0][0].all[0][0].the[1][0]': 'down'
}
Dot.dot(obj).should.eql(expected)
})
it('useBrackets wrap indexes without brackets', function () {
var expected = {
id: 'my-id',
'nes.ted.value': true,
'other.nested.stuff': 5,
'nested.array.0.with': 'object1',
'nested.array.1.and': 'object2',
'some.array.0': 'A',
'some.array.1': 'B',
ehrm: 123,
'dates.first': new Date('Mon Oct 13 2014 00:00:00 GMT+0100 (BST)'),
'arrays.0.0.0.all.0.0.the.0': 'way',
'arrays.0.0.0.all.0.0.the.1.0': 'down'
}
Dot.useBrackets = false
Dot.dot(obj).should.eql(expected)
Dot.useBrackets = true
})
it('Always keeps empty arrays', function () {
Dot.dot({ hello: [] }).should.eql({ hello: [] })
Dot.dot({ hello: { world: [] } }).should.eql({ 'hello.world': [] })
})
it('Always keeps empty objects', function () {
Dot.dot({ hello: {} }).should.eql({ hello: {} })
Dot.dot({ hello: { world: {} } }).should.eql({ 'hello.world': {} })
})
})

View file

@ -0,0 +1,46 @@
{
"name": "dot-object",
"description": "dot-object makes it possible to transform and read (JSON) objects using dot notation.",
"version": "1.1.0",
"author": {
"name": "Rob Halff",
"email": "rob.halff@gmail.com"
},
"repository": {
"type": "git",
"url": "git://github.com/rhalff/dot-object.git"
},
"bugs": {
"url": "https://github.com/rhalff/dot-object/issues"
},
"main": "index",
"bin": "./bin/dot-object",
"scripts": {
"test": "gulp test"
},
"devDependencies": {
"gulp": "^3.9.0",
"gulp-beautify": "^1.1.2",
"gulp-headerfooter": "^1.0.3",
"gulp-jscs": "^2.0.0",
"gulp-jshint": "^1.11.2",
"gulp-mocha": "^2.1.3",
"gulp-rename": "^1.2.2",
"gulp-uglify": "^1.2.0",
"gulp-util": "^3.0.6",
"jscs": "^2.0.0",
"jscs-jsdoc": "1.1.0",
"mocha": "2.x.x"
},
"keywords": [
"json",
"filter",
"transform",
"dot notation",
"dot"
],
"dependencies": {
"commander": "^2.8.1",
"glob": "^5.0.14"
}
}

View file

@ -0,0 +1,96 @@
'use strict'
require('should')
var Dot = require('../index')
describe('Should be able to merge:', function () {
it('to property', function () {
var link = {
other: {
three: 'Three Things',
four: 'Four Things'
},
things: {
one: 'One Thing',
two: 'Two Things'
}
}
var expected = {
things: {
one: 'One Thing',
two: 'Two Things',
three: 'Three Things',
four: 'Four Things'
}
}
Dot.move('other', 'things', link, true)
link.should.eql(expected)
})
it('to nested property', function () {
var link = {
other: {
three: 'Three Things',
four: 'Four Things'
},
things: {
one: 'One Thing',
two: 'Two Things',
target: {
im: 'already here'
}
}
}
var expected = {
things: {
one: 'One Thing',
two: 'Two Things',
target: {
im: 'already here',
three: 'Three Things',
four: 'Four Things'
}
}
}
Dot.move('other', 'things.target', link, true)
link.should.eql(expected)
})
it('array to array', function () {
var link = {
other: [
'Three Things',
'Four Things'
],
things: {
one: 'One Thing',
two: 'Two Things',
target: [
'already here'
]
}
}
var expected = {
things: {
one: 'One Thing',
two: 'Two Things',
target: [
'already here',
'Three Things',
'Four Things'
]
}
}
Dot.move('other', 'things.target', link, true)
link.should.eql(expected)
})
})

View file

@ -0,0 +1,70 @@
'use strict'
require('should')
var Dot = require('../index')
describe('Move test:', function () {
it('Should be able to move properties', function () {
var link = {
id: '527423a65e380f0000588e47',
source: '526dd5c6b4c4aa8770000001',
target: '527402d6b15d1800008755cf',
out: 'github',
in: 'in'
}
var expected = {
id: '527423a65e380f0000588e47',
source: { id: '526dd5c6b4c4aa8770000001', port: 'github' },
target: { id: '527402d6b15d1800008755cf', port: 'in' }
}
Dot.move('source', 'source.id', link)
Dot.move('out', 'source.port', link)
Dot.move('target', 'target.id', link)
Dot.move('in', 'target.port', link)
link.should.eql(expected)
})
it('Undefined properties should be ignored', function () {
var link = {
source: '526dd5c6b4c4aa8770000001',
target: '527402d6b15d1800008755cf',
out: 'github',
in: 'in'
}
var expected = {
source: { id: '526dd5c6b4c4aa8770000001' },
target: { port: 'in' },
out: 'github'
}
Dot.move('source', 'source.id', link)
Dot.move('out.er.nope', 'source.port', link)
Dot.move('target.bla.di.bla', 'target.id', link)
Dot.move('in', 'target.port', link)
link.should.eql(expected)
})
it('Should process modifiers', function () {
var link = {
source: 'one',
target: 'two'
}
var expected = {
source: { id: 'ONE' },
target: { port: 'TWO' }
}
function up (val) { return val.toUpperCase() }
Dot.move('source', 'source.id', link, up)
Dot.move('target', 'target.port', link, up)
link.should.eql(expected)
})
})

View file

@ -0,0 +1,93 @@
'use strict'
require('should')
var _s = require('underscore.string')
var Dot = require('../index')
describe('Override test:', function () {
it('Redefinition should _not_ fail if override is true', function () {
var dot = new Dot('.', true)
var obj = {
some: 'value',
already: 'set'
}
dot.str('already.new', 'value', obj)
obj.should.eql({
some: 'value',
already: { new: 'value' }
})
})
it('Redefinition should _not_ fail if override is true (2)', function () {
var dot = new Dot('.', true)
var obj = {
some: 'value',
already: 'set'
}
dot.str('already.new', 'value', obj)
dot.str('some', 'new_value', obj)
obj.should.eql({
some: 'new_value',
already: { new: 'value' }
})
})
it('Allow override even when target is non-empty object',
function () {
var obj = {
sample: {
dotted: {
bar: {
baz: 'baz'
}
}
}
}
Dot.override = true
Dot.str('sample.dotted.bar', { baz: 'boom' }, obj)
Dot.override = false
obj.should.eql({
sample: {
dotted: {
bar: {
baz: 'boom'
}
}
}
})
}
)
it('should process non dot notation value with modifier if override is true',
function () {
var dot = new Dot('.', true)
var row = {
title: 'my page',
slug: 'My Page'
}
var mods = {
title: _s.titleize,
slug: _s.slugify
}
dot.object(row, mods)
row.should.eql({
title: 'My Page',
slug: 'my-page'
})
}
)
})

View file

@ -0,0 +1,134 @@
'use strict'
/* jshint -W030 */
require('should')
var Dot = require('../index')
describe('Pick:', function () {
it('Should be able to pick a value', function () {
var obj = {
some: 'value',
already: 'set'
}
var val = Dot.pick('some', obj)
val.should.eql('value')
})
it('Should be able to pick dotted value', function () {
var obj = {
some: {
other: 'value'
}
}
var val = Dot.pick('some.other', obj)
val.should.eql('value')
})
it('Should be able to pick null properties', function () {
var obj = {
some: null
}
var val = Dot.pick('some', obj)
;(val === null).should.equal(true)
})
it('Should return undefined when picking an non-existing value', function () {
var obj = {
some: null
}
var val = Dot.pick('other', obj)
;(val === undefined).should.equal(true)
})
it('Should return undefined when picking an non-existing dotted value',
function () {
var obj = {
some: null
}
var val = Dot.pick('some.other', obj)
;(val === undefined).should.equal(true)
}
)
it("Should check down the object's prototype chain", function () {
var obj = {
some: {
other: 'value'
}
}
var objIns = Object.create(obj)
objIns.should.have.property('some')
var val = Dot.pick('some.other', objIns)
val.should.be.instanceOf(String)
})
it('Should be able to delete picked value', function () {
var obj = {
some: {
other: 'value',
foo: 'bar'
}
}
var val = Dot.pick('some.foo', obj, true)
val.should.eql('bar')
obj.should.eql({
some: {
other: 'value'
}
})
})
it('Should be able to delete picked array value', function () {
var obj = {
some: {
other: 'value',
arrayItems: ['foo', 'bar', 'baz']
}
}
var val = Dot.pick('some.arrayItems[1]', obj, true)
val.should.eql('bar')
obj.should.eql({
some: {
other: 'value',
arrayItems: ['foo', , 'baz'] /* eslint-disable-line no-sparse-arrays */
}
})
})
it('Should be able to delete picked array value and reindex', function () {
var obj = {
some: {
other: 'value',
arrayItems: ['foo', 'bar', 'baz']
}
}
var val = Dot.pick('some.arrayItems[1]', obj, true, true)
val.should.eql('bar')
obj.should.eql({
some: {
other: 'value',
arrayItems: ['foo', 'baz']
}
})
})
})

View file

@ -0,0 +1,80 @@
'use strict'
require('should')
var Dot = require('../index')
describe('Remove/del:', function () {
var obj
var expected
beforeEach(function () {
obj = {
id: 'my-id',
nes: {
ted: {
gone: 'value',
still: 'there'
}
},
ehrm: 123
}
expected = {
id: 'my-id',
nes: {
ted: {
still: 'there'
}
}
}
})
it('Should be able to remove() properties', function () {
Dot.remove('ehrm', obj).should.equal(123)
Dot.remove('nes.ted.gone', obj).should.equal('value')
obj.should.eql(expected)
})
it('Should be able to use del() alias', function () {
Dot.del('ehrm', obj).should.equal(123)
Dot.del('nes.ted.gone', obj).should.equal('value')
obj.should.eql(expected)
})
it('Should be able to remove() array item and reindex array', function () {
var obj = {
some: {
other: 'value',
arrayItems: ['foo', 'bar', 'baz']
}
}
var val = Dot.remove('some.arrayItems[1]', obj, true, true)
val.should.eql('bar')
obj.should.eql({
some: {
other: 'value',
arrayItems: ['foo', 'baz']
}
})
})
it('Should be handle being told to reindex an object by ignoring reindex rule', function () {
var obj = {
some: {
other: 'value',
arrayItems: ['foo', 'bar', 'baz']
}
}
var val = Dot.remove('some.other', obj, true, true)
val.should.eql('value')
obj.should.eql({
some: {
arrayItems: ['foo', 'bar', 'baz']
}
})
})
})

View file

@ -0,0 +1,63 @@
'use strict'
require('should')
var Dot = require('../index')
describe('str:', function () {
it('can set root property', function () {
Dot.str('b', 2, {
a: 1
}).should.deepEqual({
a: 1,
b: 2
})
})
it('can set nested property', function () {
Dot.str('b.a', 2, {
a: 1
}).should.deepEqual({
a: 1,
b: {
a: 2
}
})
})
it('can set nested with array notation', function () {
var obj = {
a: 1
}
Dot.str('object.fields[0].subfield', 'value', obj)
Dot.str('object.fields[1].subfield', 'value1', obj)
obj.should.deepEqual({
a: 1,
object: {
fields: [
{
subfield: 'value'
},
{
subfield: 'value1'
}
]
}
})
})
it('can set root level property regardless whether override is set', function () {
Dot.str('a', 'b', {
a: 1
}).should.deepEqual({
a: 'b'
})
})
it('cannot set __proto__ property', function () {
(() => Dot.str('__proto__.toString', 'hi', {})).should.throw(
/Refusing to update/
);
({}.toString().should.deepEqual('[object Object]'))
})
})

View file

@ -0,0 +1,42 @@
'use strict'
require('should')
var Dot = require('../index')
var testData = require('./data')
function singleTest (dot, input, expected) {
dot.object(input)
JSON.stringify(input).should.eql(JSON.stringify(expected))
}
describe('Test Data:', function () {
var dot = new Dot()
function testIt (test) {
it(test.name, function () {
if (test.options) {
Object.keys(test.options).forEach(function (name) {
dot[name] = test.options[name]
})
}
if (Array.isArray(test.input)) {
if (
!Array.isArray(test.expected) ||
test.input.length !== test.expected.length
) {
throw Error('Input and Expected tests length must be the same')
}
test.expected.forEach((expected, i) => {
singleTest(dot, test.input[i], expected)
})
} else {
singleTest(dot, test.input, test.expected)
}
})
}
// note with object() it is possible to cleanup, with del it is not.
testData.forEach(testIt)
})

View file

@ -0,0 +1,28 @@
'use strict'
require('should')
var Dot = require('../index')
var testData = [
require('./transforms/twitter'),
require('./transforms/contact')
]
describe('Test Transforms:', function () {
var dot = new Dot()
function testIt (test) {
it(test.name, function () {
if (test.options) {
Object.keys(test.options).forEach(function (name) {
dot[name] = test.options[name]
})
}
var tgt1 = {}
var tgt2 = dot.transform(test.transform, test.input, tgt1)
JSON.stringify(tgt1).should.eql(JSON.stringify(test.expected))
JSON.stringify(tgt2).should.eql(JSON.stringify(test.expected))
})
}
testData.forEach(testIt)
})

View file

@ -0,0 +1,81 @@
'use strict'
require('should')
var Dot = require('../index')
describe('Transfer:', function () {
it('Should be able to transfer properties', function () {
var src = {
name: 'John',
stuff: {
phone: {
brand: 'iphone',
version: 6
}
}
}
var tgt = {
name: 'Brandon'
}
var srcExpected = { name: 'John', stuff: {} }
var tgtExpected = {
name: 'Brandon',
wanna: {
haves: {
phone: {
brand: 'iphone',
version: 6
}
}
}
}
Dot.transfer('stuff.phone', 'wanna.haves.phone', src, tgt)
src.should.eql(srcExpected)
tgt.should.eql(tgtExpected)
})
it('Should process modifiers', function () {
var up = function (val) {
val.brand = val.brand.toUpperCase()
return val
}
var src = {
name: 'John',
stuff: {
phone: {
brand: 'iphone',
version: 6
}
}
}
var tgt = {
name: 'Brandon'
}
var srcExpected = { name: 'John', stuff: {} }
var tgtExpected = {
name: 'Brandon',
wanna: {
haves: {
phone: {
brand: 'IPHONE',
version: 6
}
}
}
}
Dot.transfer('stuff.phone', 'wanna.haves.phone', src, tgt, up)
src.should.eql(srcExpected)
tgt.should.eql(tgtExpected)
})
})

View file

@ -0,0 +1,26 @@
{
"name": "Contact Transform",
"input": {
"id": 1,
"contact": {
"firstName": "John",
"lastName": "Doe",
"email": "example@test.com"
}
},
"transform": {
"id": "nr",
"contact.firstName": "name.first",
"contact.lastName": "name.last",
"contact.email": "email"
},
"expected": {
"nr": 1,
"name": {
"first": "John",
"last": "Doe"
},
"email": "example@test.com"
}
}

View file

@ -0,0 +1,174 @@
{
"name": "Twitter Transform",
"input": {
"text": "RT @PostGradProblem: In preparation for the NFL lockout, I will be spending twice as much time analyzing my fantasy baseball team during ...",
"truncated": true,
"in_reply_to_user_id": null,
"in_reply_to_status_id": null,
"favorited": false,
"source": "<a href=\"http://twitter.com/\" rel=\"nofollow\">Twitter for iPhone</a>",
"in_reply_to_screen_name": null,
"in_reply_to_status_id_str": null,
"id_str": "54691802283900928",
"entities": {
"user_mentions": [
{
"indices": [
3,
19
],
"screen_name": "PostGradProblem",
"id_str": "271572434",
"name": "PostGradProblems",
"id": 271572434
}
],
"urls": [
],
"hashtags": [
]
},
"contributors": null,
"retweeted": false,
"in_reply_to_user_id_str": null,
"place": null,
"retweet_count": 4,
"created_at": "Sun Apr 03 23:48:36 +0000 2011",
"retweeted_status": {
"text": "In preparation for the NFL lockout, I will be spending twice as much time analyzing my fantasy baseball team during company time. #PGP",
"truncated": false,
"in_reply_to_user_id": null,
"in_reply_to_status_id": null,
"favorited": false,
"source": "<a href=\"http://www.hootsuite.com\" rel=\"nofollow\">HootSuite</a>",
"in_reply_to_screen_name": null,
"in_reply_to_status_id_str": null,
"id_str": "54640519019642881",
"entities": {
"user_mentions": [
],
"urls": [
],
"hashtags": [
{
"text": "PGP",
"indices": [
130,
134
]
}
]
},
"contributors": null,
"retweeted": false,
"in_reply_to_user_id_str": null,
"place": null,
"retweet_count": 4,
"created_at": "Sun Apr 03 20:24:49 +0000 2011",
"user": {
"notifications": null,
"profile_use_background_image": true,
"statuses_count": 31,
"profile_background_color": "C0DEED",
"followers_count": 3066,
"profile_image_url": "http://a2.twimg.com/profile_images/1285770264/PGP_normal.jpg",
"listed_count": 6,
"profile_background_image_url": "http://a3.twimg.com/a/1301071706/images/themes/theme1/bg.png",
"description": "",
"screen_name": "PostGradProblem",
"default_profile": true,
"verified": false,
"time_zone": null,
"profile_text_color": "333333",
"is_translator": false,
"profile_sidebar_fill_color": "DDEEF6",
"location": "",
"id_str": "271572434",
"default_profile_image": false,
"profile_background_tile": false,
"lang": "en",
"friends_count": 21,
"protected": false,
"favourites_count": 0,
"created_at": "Thu Mar 24 19:45:44 +0000 2011",
"profile_link_color": "0084B4",
"name": "PostGradProblems",
"show_all_inline_media": false,
"follow_request_sent": null,
"geo_enabled": false,
"profile_sidebar_border_color": "C0DEED",
"url": null,
"id": 271572434,
"contributors_enabled": false,
"following": null,
"utc_offset": null
},
"id": 54640519019642880,
"coordinates": null,
"geo": null
},
"user": {
"notifications": null,
"profile_use_background_image": true,
"statuses_count": 351,
"profile_background_color": "C0DEED",
"followers_count": 48,
"profile_image_url": "http://a1.twimg.com/profile_images/455128973/gCsVUnofNqqyd6tdOGevROvko1_500_normal.jpg",
"listed_count": 0,
"profile_background_image_url": "http://a3.twimg.com/a/1300479984/images/themes/theme1/bg.png",
"description": "watcha doin in my waters?",
"screen_name": "OldGREG85",
"default_profile": true,
"verified": false,
"time_zone": "Hawaii",
"profile_text_color": "333333",
"is_translator": false,
"profile_sidebar_fill_color": "DDEEF6",
"location": "Texas",
"id_str": "80177619",
"default_profile_image": false,
"profile_background_tile": false,
"lang": "en",
"friends_count": 81,
"protected": false,
"favourites_count": 0,
"created_at": "Tue Oct 06 01:13:17 +0000 2009",
"profile_link_color": "0084B4",
"name": "GG",
"show_all_inline_media": false,
"follow_request_sent": null,
"geo_enabled": false,
"profile_sidebar_border_color": "C0DEED",
"url": null,
"id": 80177619,
"contributors_enabled": false,
"following": null,
"utc_offset": -36000
},
"id": 54691802283900930,
"coordinates": null,
"geo": null
},
"transform": {
"text": "content",
"created_at": "time",
"user.location": "location",
"user.favourites_count": "stats.favs",
"user.followers_count": "stats.followers",
"user.statuses_count": "stats.statuses"
},
"expected": {
"content": "RT @PostGradProblem: In preparation for the NFL lockout, I will be spending twice as much time analyzing my fantasy baseball team during ...",
"time": "Sun Apr 03 23:48:36 +0000 2011",
"location": "Texas",
"stats": {
"favs": 0,
"followers": 48,
"statuses": 351
}
}
}

View file

@ -0,0 +1,68 @@
'use strict'
require('should')
var Dot = require('../index')
describe('useArray:', function () {
var dotObject, arrayObject, object
var arrayObjectExpected, objectExpected, dotObjectExpected
beforeEach(function () {
dotObject = {
'a.0': 'value'
}
dotObjectExpected = {
'a.0': 'value'
}
arrayObject = {
a: ['value']
}
arrayObjectExpected = {
a: ['value']
}
object = {
a: {
0: 'value'
}
}
objectExpected = {
a: {
0: 'value'
}
}
})
it('default is using array ', function () {
var dotLocal = require('../index')
arrayObjectExpected.should.eql(dotLocal.object(dotObject))
})
it('Should convert dot using arrays ', function () {
Dot.useArray = true
arrayObjectExpected.should.eql(Dot.object(dotObject))
})
it('Should convert dot not using arrays ', function () {
Dot.useArray = false
objectExpected.should.eql(Dot.object(dotObject))
})
it('Should convert object using arrays ', function () {
Dot.useArray = true
arrayObjectExpected.should.eql(Dot.object(dotObject))
})
it('Should convert dot using arrays and convert back equals source', function () {
Dot.useArray = true
Dot.useBrackets = false
dotObjectExpected.should.eql(Dot.dot(Dot.object(dotObject)))
})
it('Should convert object using arrays and convert back equals source', function () {
Dot.useArray = true
arrayObjectExpected.should.eql(Dot.object(Dot.dot(object)))
})
it('Should convert dot not using arrays and convert back equals source', function () {
Dot.useArray = false
dotObjectExpected.should.eql(Dot.dot(Dot.object(dotObject)))
})
it('Should convert object not using arrays and convert back equals source', function () {
Dot.useArray = false
objectExpected.should.eql(Dot.object(Dot.dot(arrayObject)))
})
})