object-path
Advanced tools
Comparing version 0.10.0 to 0.11.0
351
index.js
@@ -63,215 +63,228 @@ (function (root, factory){ | ||
function set(obj, path, value, doNotReplace){ | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} | ||
if (!path || path.length === 0) { | ||
return obj; | ||
} | ||
if (typeof path === 'string') { | ||
return set(obj, path.split('.').map(getKey), value, doNotReplace); | ||
} | ||
var currentPath = path[0]; | ||
function factory(options) { | ||
options = options || {} | ||
if (path.length === 1) { | ||
var oldVal = obj[currentPath]; | ||
if (oldVal === void 0 || !doNotReplace) { | ||
obj[currentPath] = value; | ||
var objectPath = function(obj) { | ||
return Object.keys(objectPath).reduce(function(proxy, prop) { | ||
if(prop === 'create') { | ||
return proxy; | ||
} | ||
/*istanbul ignore else*/ | ||
if (typeof objectPath[prop] === 'function') { | ||
proxy[prop] = objectPath[prop].bind(objectPath, obj); | ||
} | ||
return proxy; | ||
}, {}); | ||
}; | ||
function getShallowProperty(obj, prop) { | ||
if(options.includeInheritedProps || (typeof prop === 'number' && Array.isArray(obj)) || obj.hasOwnProperty(prop)) { | ||
return obj[prop]; | ||
} | ||
return oldVal; | ||
} | ||
if (obj[currentPath] === void 0) { | ||
//check if we assume an array | ||
if(typeof path[1] === 'number') { | ||
obj[currentPath] = []; | ||
} else { | ||
obj[currentPath] = {}; | ||
function set(obj, path, value, doNotReplace){ | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} | ||
} | ||
if (!path || path.length === 0) { | ||
return obj; | ||
} | ||
if (typeof path === 'string') { | ||
return set(obj, path.split('.').map(getKey), value, doNotReplace); | ||
} | ||
var currentPath = path[0]; | ||
var currentValue = getShallowProperty(obj, currentPath); | ||
if (path.length === 1) { | ||
if (currentValue === void 0 || !doNotReplace) { | ||
obj[currentPath] = value; | ||
} | ||
return currentValue; | ||
} | ||
return set(obj[currentPath], path.slice(1), value, doNotReplace); | ||
} | ||
if (currentValue === void 0) { | ||
//check if we assume an array | ||
if(typeof path[1] === 'number') { | ||
obj[currentPath] = []; | ||
} else { | ||
obj[currentPath] = {}; | ||
} | ||
} | ||
function del(obj, path) { | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
return set(obj[currentPath], path.slice(1), value, doNotReplace); | ||
} | ||
if (isEmpty(obj)) { | ||
return void 0; | ||
} | ||
objectPath.has = function (obj, path) { | ||
if (obj == null) { | ||
return false; | ||
} | ||
if (isEmpty(path)) { | ||
return obj; | ||
} | ||
if(typeof path === 'string') { | ||
return del(obj, path.split('.')); | ||
} | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} else if (typeof path === 'string') { | ||
path = path.split('.'); | ||
} | ||
var currentPath = getKey(path[0]); | ||
var oldVal = obj[currentPath]; | ||
if (!path || path.length === 0) { | ||
return false; | ||
} | ||
if(path.length === 1) { | ||
if (oldVal !== void 0) { | ||
if (isArray(obj)) { | ||
obj.splice(currentPath, 1); | ||
for (var i = 0; i < path.length; i++) { | ||
var j = getKey(path[i]); | ||
if((typeof j === 'number' && isArray(obj) && j < obj.length) || | ||
(options.includeInheritedProps ? (j in Object(obj)) : _hasOwnProperty.call(obj, j))) { | ||
obj = obj[j]; | ||
} else { | ||
delete obj[currentPath]; | ||
return false; | ||
} | ||
} | ||
} else { | ||
if (obj[currentPath] !== void 0) { | ||
return del(obj[currentPath], path.slice(1)); | ||
} | ||
} | ||
return obj; | ||
} | ||
return true; | ||
}; | ||
var objectPath = function(obj) { | ||
return Object.keys(objectPath).reduce(function(proxy, prop) { | ||
/*istanbul ignore else*/ | ||
if (typeof objectPath[prop] === 'function') { | ||
proxy[prop] = objectPath[prop].bind(objectPath, obj); | ||
} | ||
objectPath.ensureExists = function (obj, path, value){ | ||
return set(obj, path, value, true); | ||
}; | ||
return proxy; | ||
}, {}); | ||
}; | ||
objectPath.set = function (obj, path, value, doNotReplace){ | ||
return set(obj, path, value, doNotReplace); | ||
}; | ||
objectPath.has = function (obj, path) { | ||
if (isEmpty(obj)) { | ||
return false; | ||
} | ||
objectPath.insert = function (obj, path, value, at){ | ||
var arr = objectPath.get(obj, path); | ||
at = ~~at; | ||
if (!isArray(arr)) { | ||
arr = []; | ||
objectPath.set(obj, path, arr); | ||
} | ||
arr.splice(at, 0, value); | ||
}; | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} else if (typeof path === 'string') { | ||
path = path.split('.'); | ||
} | ||
objectPath.empty = function(obj, path) { | ||
if (isEmpty(path)) { | ||
return void 0; | ||
} | ||
if (obj == null) { | ||
return void 0; | ||
} | ||
if (isEmpty(path) || path.length === 0) { | ||
return false; | ||
} | ||
var value, i; | ||
if (!(value = objectPath.get(obj, path))) { | ||
return void 0; | ||
} | ||
for (var i = 0; i < path.length; i++) { | ||
var j = path[i]; | ||
if ((isObject(obj) || isArray(obj)) && _hasOwnProperty.call(obj, j)) { | ||
obj = obj[j]; | ||
if (typeof value === 'string') { | ||
return objectPath.set(obj, path, ''); | ||
} else if (isBoolean(value)) { | ||
return objectPath.set(obj, path, false); | ||
} else if (typeof value === 'number') { | ||
return objectPath.set(obj, path, 0); | ||
} else if (isArray(value)) { | ||
value.length = 0; | ||
} else if (isObject(value)) { | ||
for (i in value) { | ||
if (_hasOwnProperty.call(value, i)) { | ||
delete value[i]; | ||
} | ||
} | ||
} else { | ||
return false; | ||
return objectPath.set(obj, path, null); | ||
} | ||
} | ||
}; | ||
return true; | ||
}; | ||
objectPath.push = function (obj, path /*, values */){ | ||
var arr = objectPath.get(obj, path); | ||
if (!isArray(arr)) { | ||
arr = []; | ||
objectPath.set(obj, path, arr); | ||
} | ||
objectPath.ensureExists = function (obj, path, value){ | ||
return set(obj, path, value, true); | ||
}; | ||
arr.push.apply(arr, Array.prototype.slice.call(arguments, 2)); | ||
}; | ||
objectPath.set = function (obj, path, value, doNotReplace){ | ||
return set(obj, path, value, doNotReplace); | ||
}; | ||
objectPath.coalesce = function (obj, paths, defaultValue) { | ||
var value; | ||
objectPath.insert = function (obj, path, value, at){ | ||
var arr = objectPath.get(obj, path); | ||
at = ~~at; | ||
if (!isArray(arr)) { | ||
arr = []; | ||
objectPath.set(obj, path, arr); | ||
} | ||
arr.splice(at, 0, value); | ||
}; | ||
for (var i = 0, len = paths.length; i < len; i++) { | ||
if ((value = objectPath.get(obj, paths[i])) !== void 0) { | ||
return value; | ||
} | ||
} | ||
objectPath.empty = function(obj, path) { | ||
if (isEmpty(path)) { | ||
return obj; | ||
} | ||
if (isEmpty(obj)) { | ||
return void 0; | ||
} | ||
return defaultValue; | ||
}; | ||
var value, i; | ||
if (!(value = objectPath.get(obj, path))) { | ||
return obj; | ||
} | ||
objectPath.get = function (obj, path, defaultValue){ | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} | ||
if (!path || path.length === 0) { | ||
return obj; | ||
} | ||
if (obj == null) { | ||
return defaultValue; | ||
} | ||
if (typeof path === 'string') { | ||
return objectPath.get(obj, path.split('.'), defaultValue); | ||
} | ||
if (typeof value === 'string') { | ||
return objectPath.set(obj, path, ''); | ||
} else if (isBoolean(value)) { | ||
return objectPath.set(obj, path, false); | ||
} else if (typeof value === 'number') { | ||
return objectPath.set(obj, path, 0); | ||
} else if (isArray(value)) { | ||
value.length = 0; | ||
} else if (isObject(value)) { | ||
for (i in value) { | ||
if (_hasOwnProperty.call(value, i)) { | ||
delete value[i]; | ||
} | ||
var currentPath = getKey(path[0]); | ||
var nextObj = getShallowProperty(obj, currentPath) | ||
if (nextObj === void 0) { | ||
return defaultValue; | ||
} | ||
} else { | ||
return objectPath.set(obj, path, null); | ||
} | ||
}; | ||
objectPath.push = function (obj, path /*, values */){ | ||
var arr = objectPath.get(obj, path); | ||
if (!isArray(arr)) { | ||
arr = []; | ||
objectPath.set(obj, path, arr); | ||
} | ||
if (path.length === 1) { | ||
return nextObj; | ||
} | ||
arr.push.apply(arr, Array.prototype.slice.call(arguments, 2)); | ||
}; | ||
return objectPath.get(obj[currentPath], path.slice(1), defaultValue); | ||
}; | ||
objectPath.coalesce = function (obj, paths, defaultValue) { | ||
var value; | ||
objectPath.del = function del(obj, path) { | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} | ||
for (var i = 0, len = paths.length; i < len; i++) { | ||
if ((value = objectPath.get(obj, paths[i])) !== void 0) { | ||
return value; | ||
if (obj == null) { | ||
return obj; | ||
} | ||
} | ||
return defaultValue; | ||
}; | ||
if (isEmpty(path)) { | ||
return obj; | ||
} | ||
if(typeof path === 'string') { | ||
return objectPath.del(obj, path.split('.')); | ||
} | ||
objectPath.get = function (obj, path, defaultValue){ | ||
if (typeof path === 'number') { | ||
path = [path]; | ||
} | ||
if (!path || path.length === 0) { | ||
return obj; | ||
} | ||
if (obj == null) { | ||
return defaultValue; | ||
} | ||
if (typeof path === 'string') { | ||
return objectPath.get(obj, path.split('.'), defaultValue); | ||
} | ||
var currentPath = getKey(path[0]); | ||
var currentVal = getShallowProperty(obj, currentPath); | ||
if(currentVal == null) { | ||
return currentVal; | ||
} | ||
var currentPath = getKey(path[0]); | ||
var nextObj | ||
if((typeof currentPath === 'number' && Array.isArray(obj)) || obj.hasOwnProperty(currentPath)) { | ||
nextObj = obj[currentPath] | ||
} | ||
if (nextObj === void 0) { | ||
return defaultValue; | ||
} | ||
if(path.length === 1) { | ||
if (isArray(obj)) { | ||
obj.splice(currentPath, 1); | ||
} else { | ||
delete obj[currentPath]; | ||
} | ||
} else { | ||
if (obj[currentPath] !== void 0) { | ||
return objectPath.del(obj[currentPath], path.slice(1)); | ||
} | ||
} | ||
if (path.length === 1) { | ||
return nextObj; | ||
return obj; | ||
} | ||
return objectPath.get(obj[currentPath], path.slice(1), defaultValue); | ||
}; | ||
return objectPath; | ||
} | ||
objectPath.del = function(obj, path) { | ||
return del(obj, path); | ||
}; | ||
return objectPath; | ||
var mod = factory(); | ||
mod.create = factory; | ||
mod.withInheritedProps = factory({includeInheritedProps: true}) | ||
return mod; | ||
}); |
{ | ||
"name": "object-path", | ||
"description": "Access deep object properties using a path", | ||
"version": "0.10.0", | ||
"version": "0.11.0", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Mario Casciaro" |
@@ -17,2 +17,8 @@ | ||
### 0.11.0 | ||
* Introduce ability to specify options and create new instances of `object-path` | ||
* Introduce option to control the way `object-path` deals with inherited properties (`includeInheritedProps`) | ||
* New default `object-path` instance already configured to handle not-own object properties (`withInheritedProps`) | ||
### 0.10.0 | ||
@@ -22,3 +28,3 @@ | ||
* Introduced a benchmarking test suite | ||
* **BREAKING CHANGE**: `del` will not delete not-own properties | ||
* **BREAKING CHANGE**: `del`, `empty`, `set` will not affect not-own object's properties (made them consistent with the other methods) | ||
@@ -122,6 +128,46 @@ ## Install | ||
``` | ||
### Notes | ||
### How `object-path` deals with inherited properties | ||
`object-path` is intentionally designed to access only an object's own properties | ||
By default `object-path` will only access an object's own properties. Look at the following example: | ||
```javascript | ||
var Obj = function() {}; | ||
Obj.prototype.notOwn = {prop: 'a'}; | ||
var obj = new Obj(); | ||
//This will return undefined (or the default value you specified), because notOwn is | ||
//an inherited property | ||
objectPath.get(obj, 'notOwn.prop'); | ||
//This will set the property on the obj instance and not the prototype. | ||
//In other words Obj.notOwn.prop === 'a' and obj.notOwn.prop === 'b' | ||
objectPath.set(obj, 'notOwn.prop', 'b'); | ||
``` | ||
To configure `object-path` to also deal with inherited properties, you need to create a new instance and specify | ||
the `includeInheritedProps = true` in the options object: | ||
```javascript | ||
var objectPath = require("object-path"); | ||
var objectPathWithInheritedProps = objectPath.create({includeInheritedProps: true}) | ||
``` | ||
Alternatively, `object-path` exposes an instance already configured to handle inherited properties (`objectPath.withInheritedProps`): | ||
```javascript | ||
var objectPath = require("object-path"); | ||
var objectPathWithInheritedProps = objectPath.withInheritedProps | ||
``` | ||
Once you have the new instance, you can access inherited properties as you access other properties: | ||
```javascript | ||
var Obj = function() {}; | ||
Obj.prototype.notOwn = {prop: 'a'}; | ||
var obj = new Obj(); | ||
//This will return 'a' | ||
objectPath.withInheritedProps.get(obj, 'notOwn.prop'); | ||
//This will set Obj.notOwn.prop to 'b' | ||
objectPath.set(obj, 'notOwn.prop', 'b'); | ||
``` | ||
### Immutability | ||
@@ -128,0 +174,0 @@ |
145
test.js
@@ -182,2 +182,7 @@ 'use strict'; | ||
expect(obj).to.have.deep.property('b.e.1.g', 'f'); | ||
obj = {} | ||
objectPath.set(obj, 'b.0', 'a'); | ||
objectPath.set(obj, 'b.1', 'b'); | ||
expect(obj.b).to.be.deep.equal(['a', 'b']); | ||
}); | ||
@@ -369,7 +374,7 @@ | ||
expect(objectPath.empty(obj, 'path')).to.equal(void 0); | ||
expect(objectPath.empty(obj, '')).to.equal(obj); | ||
expect(objectPath.empty(obj, '')).to.equal(void 0); | ||
obj.path = true; | ||
expect(objectPath.empty(obj, 'inexistant')).to.equal(obj); | ||
expect(objectPath.empty(obj, 'inexistant')).to.equal(void 0); | ||
}); | ||
@@ -436,6 +441,2 @@ | ||
describe('del', function(){ | ||
it('should return undefined on empty object', function(){ | ||
expect(objectPath.del({}, 'a')).to.equal(void 0); | ||
}); | ||
it('should work with number path', function(){ | ||
@@ -496,9 +497,2 @@ var obj = getTestObj(); | ||
}); | ||
it('should skip undefined paths', function(){ | ||
var obj = getTestObj(); | ||
expect(objectPath.del(obj, 'do.not.exist')).to.be.equal(obj); | ||
expect(objectPath.del(obj, 'a.c')).to.be.equal('b'); | ||
}); | ||
}); | ||
@@ -585,5 +579,11 @@ | ||
it('should test value under array', function() { | ||
var obj = getTestObj(); | ||
expect(objectPath.has(obj, 'b.d.0')).to.be.equal(true); | ||
expect(objectPath.has(obj, ['b','d',0])).to.be.equal(true); | ||
var obj = { | ||
b: ['a'] | ||
}; | ||
obj.b[3] = {o: 'a'} | ||
expect(objectPath.has(obj, 'b.0')).to.be.equal(true); | ||
expect(objectPath.has(obj, 'b.1')).to.be.equal(true); | ||
expect(objectPath.has(obj, 'b.3.o')).to.be.equal(true); | ||
expect(objectPath.has(obj, 'b.3.qwe')).to.be.equal(false); | ||
expect(objectPath.has(obj, 'b.4')).to.be.equal(false); | ||
}); | ||
@@ -778,3 +778,116 @@ | ||
}); | ||
}); | ||
describe('Don\' access not own properties [default]', function () { | ||
it('should not get a not own property', function() { | ||
var Obj = function() {}; | ||
Obj.prototype.notOwn = {a: 'a'}; | ||
var obj = new Obj(); | ||
expect(objectPath.get(obj, 'notOwn')).to.be.undefined | ||
}); | ||
it('should set a not own property on the instance (not the prototype)', function() { | ||
var proto = { | ||
notOwn: {} | ||
} | ||
var obj = Object.create(proto) | ||
objectPath.set(obj, 'notOwn.test', 'a'); | ||
expect(obj.notOwn.test).to.be.equal('a'); | ||
expect(proto.notOwn).to.be.deep.equal({}); | ||
}); | ||
it('has should return false on a not own property', function() { | ||
var proto = { | ||
notOwn: {a: 'a'} | ||
} | ||
var obj = Object.create(proto) | ||
expect(objectPath.has(obj, 'notOwn')).to.be.false; | ||
expect(objectPath.has(obj, 'notOwn.a')).to.be.false; | ||
}); | ||
it('empty should not empty on a not own property', function() { | ||
var proto = { | ||
notOwn: {a: 'a'} | ||
} | ||
var obj = Object.create(proto); | ||
objectPath.empty(obj, 'notOwn'); | ||
expect(proto.notOwn).to.be.deep.equal({a: 'a'}); | ||
expect(obj.notOwn).to.be.deep.equal({a: 'a'}); | ||
}); | ||
it('del should not delete not own property', function() { | ||
var proto = { | ||
notOwn: {a: 'a'} | ||
} | ||
var obj = Object.create(proto); | ||
objectPath.del(obj, 'notOwn.a'); | ||
expect(proto.notOwn).to.be.deep.equal({a: 'a'}); | ||
//expect(obj.notOwn).to.be.deep.equal({a: 'a'}); | ||
//objectPath.del(obj, 'notOwn'); | ||
//expect(proto).to.be.deep.equal({notOwn: {a: 'a'}}); | ||
//expect(obj).to.be.deep.equal({notOwn: {a: 'a'}}); | ||
}); | ||
}); | ||
describe('Access own properties [optional]', function () { | ||
it('should get a not own property', function() { | ||
var Obj = function() {}; | ||
Obj.prototype.notOwn = {a: 'a'}; | ||
var obj = new Obj(); | ||
expect(objectPath.withInheritedProps.get(obj, 'notOwn.a')).to.be.equal('a') | ||
}); | ||
it('should set a deep not own property on the prototype (if exists)', function() { | ||
var proto = { | ||
notOwn: {} | ||
} | ||
var obj = Object.create(proto) | ||
objectPath.withInheritedProps.set(obj, 'notOwn.test', 'a'); | ||
expect(obj.notOwn.test).to.be.equal('a'); | ||
expect(proto.notOwn).to.be.deep.equal({test: 'a'}); | ||
}); | ||
it('has should return true on a not own property', function() { | ||
var proto = { | ||
notOwn: {a: 'a'} | ||
} | ||
var obj = Object.create(proto) | ||
expect(objectPath.withInheritedProps.has(obj, 'notOwn')).to.be.true; | ||
expect(objectPath.withInheritedProps.has(obj, 'notOwn.a')).to.be.true; | ||
}); | ||
it('empty should empty a not own property', function() { | ||
var proto = { | ||
notOwn: {a: 'a'} | ||
} | ||
var obj = Object.create(proto); | ||
objectPath.withInheritedProps.empty(obj, 'notOwn'); | ||
expect(proto.notOwn).to.be.deep.equal({}); | ||
expect(obj.notOwn).to.be.deep.equal({}); | ||
}); | ||
it('del should delete a not own property', function() { | ||
var proto = { | ||
notOwn: {a: 'a'} | ||
} | ||
var obj = Object.create(proto); | ||
objectPath.withInheritedProps.del(obj, 'notOwn.a'); | ||
expect(proto.notOwn).to.be.deep.equal({}); | ||
//expect(obj.notOwn).to.be.deep.equal({}); | ||
objectPath.withInheritedProps.del(obj, 'notOwn'); | ||
//expect(proto).to.be.deep.equal({notOwn: {}}); | ||
//expect(obj).to.be.deep.equal({notOwn: {}}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43816
1069
179