mariocasciaro / object-path Goto Github PK
View Code? Open in Web Editor NEWA tiny JavaScript utility to access deep properties using a path (for Node and the Browser)
License: MIT License
A tiny JavaScript utility to access deep properties using a path (for Node and the Browser)
License: MIT License
I read about this test and don't understand why exist.
it('should ignore invalid arguments safely', function(){
var obj = {};
// 1
expect(objectPath.empty()).to.equal(void 0);
// 2
expect(objectPath.empty(obj, 'path')).to.equal(void 0);
// 3
expect(objectPath.empty(obj, '')).to.equal(obj);
obj.path = true;
// 4
expect(objectPath.empty(obj, 'inexistant')).to.equal(obj);
});
2nd an 4th assertions are essentialy the same thing.
In second assertion `obj` is an object and `path` property does not exist in `obj`.
In fourth assertion `obj` is an object and `existant` property does not exist in `obj`
I think that it should return the same value `undefined` or the same `object` passed.
Instead of:
objectPath.insert(obj, "a.c", "m", 1);
Shouldn't it be:
objectPath.insert(obj, "a.c.1", "m");
To be consistent with the rest of the API?
Hey there,
It looks like this module works in ie8. Also: after peeping the sauce it doesn't look like there would be any reason for it not to. What is the stance on browser support?
expect(objectPath.get({}, 'deep.notDefined', undefined)).to.equal(undefined);
but
try {
objectPath.get({}, 'deep.notDefined');
} catch (e) {
assert(e.toString() === 'TypeError: Cannot read property \'notDefined\' of undefined');
}
I forgot to ask one more thing, bower needs tags to be pushed. so if you could git tag 0.3.0
then git push origin --tags
, at least for the last version
I'd like to use this to determine by a boolean whether the "set" operation should take effect. Two questions:
Thanks!
Suppose I have a object like this:
const obj = { tags: [ {name: 'one'}, {name: 'two'}, {name: 'three'}, ] }
I read from the API that you can get a single value of a known index:
ObjectPath.get(obj,'tags.1.name') // returns 'two'
But does object-path allow for a path that can map the nested 'name' value?
e.g.
ObjectPath.get(obj,'tags.*.name') // returns ['one','two','three']
Thanks
I have a complex hierarchy consisting of elements identified by an ID property, which makes traversing the tree easier than by index
{
"personas": [
{ "id": "abc", "name": "John" },
{ "id:"def", "name":"Mike" },...
]
}
I would like to use a syntax like personas.id:def:name
or personas[id:def].name
to traverse the path instead of personas.1.name
.
At least to track any performance regression introduced during development
ObjectPath.get(global, 'navigator.userAgent') // undefined
const Navigator = ObjectPath.get(global, 'navigator') // Navigator
ObjectPath.get(Navigator, 'userAgent') // undefined
navigator.userAgent // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 ...
navigator['userAgent'] // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 ...
I want to set an object as follows:
let emptyObj = {}
const path = 'a.2008'
const value = [1,2,3,4]
objectPath.set(emptyObj, path, value)
I expect the object to look like:
emptyObject = {
a: {
2008: [1, 2, 3, 4],
}
}
Instead the object looks like this:
{
"a": [
null,
null,
null,
null,
null,
null,
null,
null,
null,
...(2008x),
[1, 2, 3, 4]
],
}
hey @mariocasciaro I stumbled upon a common use case (at least for me) that I need to check a series of objects and return the first non undefined value. I'm doing a really dynamic app in AngularJS and I have a huge object that has many members, that depending on the current 'object mode', I need to coalesce many objects at the same time. So I was doing this:
objectPath.coalesce(
obj,
['item.desc','plan.type.desc'],
object.coalesce(
plans,
['slug','item.title']
)
);
so, instead of having this over and over, I decided to improve it a bit, to accept an array as the first parameter (really heavy "overload" in the programming sense):
objectPath.coalesce([
[obj, ['item.desc','plan.type.desc']],
[plans, ['slug','item.title']]
], "this is ignored", null);
do you want me to PR this feature? because I'm using it 'internally' right now, not sure if people would use. the syntax is a bit shady, an array of arrays is not pretty, but it's straight forward. an object could be used, but maybe it's overkill
the code is in https://github.com/pocesar/object-path/blob/coalesce-array/index.js#L136-L145
the tests are in https://github.com/pocesar/object-path/blob/coalesce-array/test.js#L254-L280
There seems to be an issue with deleting null objects. I can't seem to find documentation on how this should be done.
I expected objectPath.del() to work on any path regardless of the value of the property.
Consider this:
var objectPath = require("object-path")
obj = { a: 1234, b: 12, c: null }
objectPath.del(obj, 'b')
// this behaves correctly as I expect
//returns { a: 1234, c: null }
// obj ==== { a: 1234, c: null }
objectPath.del(obj, 'c')
// this does not return a result nor modifies the obj
// returns null
//obj === { a: 1234, b: 12, c: null }
Likewise, I noticed .empty() has the same effect. I'm assuming internally it uses .del()
Is there anything I'm doing wrong?
Well, I found a bug with the last patch. I have an object var any = {};
.
I need it to be:
var any['1']['1'] = {};
using ensureExists like this:
var
sectionId = 1,
sections = [/* array */];
objectPath.ensureExists(any, [sectionId.toString(),sections[sectionId].questionId.toString()], {});
its yielding:
// reproducible test case
// var any = {};
// objectPath.ensureExists(any, ['1','1'], {});
// expect(any).to.be.an('object'); << ok
// expect(any[1]).to.be.an('object'); << failing
// expect(any[1][1]).to.be.an('object'); << ok
[undefined, Object{ }]
close but no cigar. it should especifically check if it's using an string and don't assume a number unless it's really a number
the version should either rollback the changes or apply an emergency patch 0.8.1
The code doesn't seem to work when the underlying object (or an object along the path) is of type Set
or Map
.
We could support both by following this approach:
Set
, convert it into an array and access it as it is an array (some logic and memory considerations need to be made here)Map
, you can simply use the get
and has
fields to access the data.Code to replicate the issue:
var objectPath = require("object-path");
var s = new Set(['a','b','c']);
console.log(objectPath.get(s, '0')); // undefined
console.log((Array.from(s))[0]); // 'a'
var m = new Map();
m.set('a','b');
console.log(objectPath.get(m, 'a')); // undefined
console.log(m.get('a')); // 'b'
I've created DefinitelyTyped/DefinitelyTyped#2584 (although I didn't finish the #18 issue yet). It's nice to have this on the readme when they accept the PR :)
I guess I wasn't thinking straight when doing this, but I had some code that did objectPath.get(obj.foo, path)
and this yields a sensible result when path is "": you get obj.foo
However, then I had code that tried to use set()
also with the same path, and that obviously does not work well because it can't change the original reference passed to the method, but probably updates a new object that gets thrown away since nothing is referencing it:
var objectPath = require('object-path');
var a = { b: { c: 1 } };
objectPath.get(a.b, ""); // returns { c: 1 }
objectPath.set(a.b, "", "hi"); // returns { c: 1 } - but a.b is still { c: 1 }, not "hi"
objectPath.set(a, "b", "hi"); // returns { c: 1 } AND a.b. is set to "hi"
As a work-around I ended up doing something like (I did this to preserve other references to the same 'obj' elsewhere):
if (path == "")
{
// quick hack for objects
for (var p in obj) delete obj[p];
for (var p in value) obj[p] = value[p];
}
else
objectPath.set(obj, path, value);
Obviously this would only work for objects and noe value types, so I'm not sure what a consistent solution here is.
Since it now just passes silently but does not do what one intends, maybe an error should be thrown instead if the path is blank?
In debian we are about to update chai from 3.5.0 to 4.1.2 and some tests are failing with object-path, see: https://bugs.debian.org/884155
Here is a sample failure:
1) set should set value under shallow object:
AssertionError: expected { Object (a, b, ...) } to have deep property 'c.m'
at Context.<anonymous> (/root/node-object-path/test.js:164:30)
at callFn (/usr/lib/nodejs/mocha/lib/runnable.js:223:21)
at Test.Runnable.run (/usr/lib/nodejs/mocha/lib/runnable.js:216:7)
at Runner.runTest (/usr/lib/nodejs/mocha/lib/runner.js:373:10)
at /usr/lib/nodejs/mocha/lib/runner.js:451:12
at next (/usr/lib/nodejs/mocha/lib/runner.js:298:14)
at /usr/lib/nodejs/mocha/lib/runner.js:308:7
at next (/usr/lib/nodejs/mocha/lib/runner.js:246:23)
at Immediate.<anonymous> (/usr/lib/nodejs/mocha/lib/runner.js:275:5)
at runCallback (timers.js:672:20)
at tryOnImmediate (timers.js:645:5)
at processImmediate [as _immediateCallback] (timers.js:617:5)
do you have an idea why ? Do you plan to move to chai 4.1.2 ?
Thanks, Paolo
code on
Lines 160 to 162 in c540af3
.has()
method:
if (isEmpty(path) || path.length === 0) {
return false;
}
If we are searching for an empty key and the variable being searched is an object, then it is intuitive that the object has that. It can perhaps miss all keys in the world as long as long as they have a name, but for keys with no name any object has all.
The delete
operator does not splice from or otherwise modify the length of arrays. It removes that key, but the other elements retain their index.
object-path's del
method instead uses splice
(index.js#L124), which modifies the length of the array and changes the index of all elements after the deleted element.
For my use case, I would like to use the normal delete
operator behavior. This would be a breaking change though, so I'm unsure what the best way of approaching this change would be.
When it is passed a simple path to set
method I expect to have the same behaviour of expression:
obj[path] = value
Examples:
var obj = {}
objectPath.set(obj, 3, 'foo') // now obj is {3: 'foo'}
// or
var obj = {}
obj[3] = 'foo'
var obj = {}
objectPath.set(obj, 'a', 'foo') // now obj is {a: 'foo'}
// or
var obj = {}
obj['a'] = 'foo' // now obj is {a: 'foo'}
But exist in set
method one case that it behaves different:
var obj = {}
objectPath.set(obj, '', 'foo') // now obj is {}
// however
var obj = {}
obj[''] = 'foo' // now obj is {'': 'foo'}
I think that it would be a good idea change behaviour in this case to keep the consistency. I think the same for another methods.
I have an object like a = { a: null }
When i try to execute objectpath.set(a, 'a.b', '3');
, it throws an error
TypeError: Cannot set property 'b' of null
Hi. I'm finding myself using coalesce
for achieving clean and idiomatic code. But sometimes my data providers return me objects, where some keys I pick present, but falsy (undefined
, null
, empty string etc). In the mean time coalesce
relies on key existing/non-existing.
Is it possible to implement in this module another variant of coalesce
which will check not key presence, but its value falsiness? So, it will work like good old object.key || object.key2 || ...
but with benefits of deep paths support.
I've looked through opened & closed issues, but found nothing similar. If it is a dulicate, please, forgive me. I think other users of object-path
could face this issue as well, and it's not pleasant to return to ||
-stuff when you're already using such powerful utility like this.
Leaving a clear way to add functionality to object-path without having to hack it (like in cases #30, #22, #21, #18), so it can be extended like lodash does. it has a lot of utility built-in, and you can for example, extend it to use fast.js using internally through _.mixin
, like this for example https://github.com/marklagendijk/lodash-deep
var _ = require("lodash");
_.mixin(require("lodash-deep"));
so whenever someone come up with a new functionality, he can just snap it in object-path without hassle. it would expose the internal functions of objectPath to the mixin constructor (like get, del, set, isEmpty, etc)
The point is, the library managed to get 164.000 downloads in a month, which is pretty nice and people are actually using it, so the argument is based on the number of people that could improve it without adding it to core
Hello! Thanks for the great module!
I'm confused with the behaviour of the .set method. I expect that when I do something like this:
objectPath.set(obj, "a.0.b", "value")
I will get the following structure:
{ a: [ { b: "value" } ] }
Instead I'm getting:
{ a: { '0': [ b: 'value' ] } }
I believe that's incorrect, because it is setting named properties of arrays treating array indices as object keys at the same time.
If this is an intended behaviour, then, what path should I pass to .set to get the structure like in the first example?
Probably, there should be a special syntax for array indices, e.g.:
objectPath.set(obj, "a[0].b", "value")
I can submit a patch for this (probably, next week), if it's OK to add this functionality.
Thank you!
Need to report it, I was using version 0.7.0 on my angular app (it's really complex, and use objectPath extensively), and it uses JSON responses as well. sometimes, JSON string numbers get translated to number literals coming from PHP. this makes objectPath.set/get (on version 0.8.0) a nightmare. Now I'm randomly getting sparse arrays on my app, so I'm having to explicitly use objectPath.set(obj, [somewhere, someId.toString()]);
everywhere =/ and now it sometimes throw, because someId
might not be defined.
another example objectPath.set(sections.display.joined,[where, id].join("."), item);
is creating the following item:
activities [undefined, undefined, undefined, 2 more...]
Because sometimes I have the where
being a full path like "some.path", so I need to join them, so it becomes "some.path.10", but the behavior changed drastically, I have near 700 calls to objectPath throrough my app, and around 500k lines, so I'll have to stick to 0.7.0 for now. I'd have to make a objectPath.ensureExist
to each intermediary object and make it an object before calling set on it, but this defeats the purpose of the module
Since object-path 1.0+ will revolve around plugins instead of PRs, what about object-path becomes an organization, and all plugins can go inside that? See #36 (comment)
https://github.com/object-path is available ;)
it can have it's own repo for it's page, better docs, can add collaborators to the team, have a finer grain for issues, etc
Hi @mariocasciaro ,
I'm a member of cdnjs, we want to host this library.
Because I found that the main file is index.js
, I'd like to discuss if it's possible to name the filename with meaningful name like object-path.js
.
Thank you.
According to the most arrogant ****head I've ever encountered in Github so far, stating "Please do more research and have something to back up your words before you waste someone's time like this." when I said that object-path already had what he is "reinventing" (littering npm with an useless module called set-value).
object-path is missing a way to handle keys with escaped dots in it. it already does when you use the array notation (eg: ['some.dot.key', 'another.path']
) and only a retarded person (IMHO) would write it as objectPath.get(obj, 'some\.****head\.is\.stupid');
sorry for the rant along, but it's amazing how people can be stupid now a days
From what I can see if I have an object key defined as a number, it is unable to be deleted using op.del
import op from 'object-path';
let users = {
'12345678': {
name: 'Mr Bob'
}
}
op.del(users, 'users.12345678'); // should delete user object
op.get(users, 'users.12345678'); // returns user object
// scenario that works
let cats = {
'user_12345678': {
name: 'Mr Meow'
}
}
op.del(cats, 'user_12345678'); // should delete user object
op.get(cats, 'user_12345678'); // returns undefined as expected
I would be happy to take this onboard and try to fix if you are still taking contributions.
hey @mariocasciaro would you like a coalesce function for object-path? Returns the first non-falsy value from an array of paths:
var obj = {
might: { have: 'this prop' },
};
objectPath.coalesce(obj, ['might.not.have', 'might.have']); // returns 'this prop'
if you'd want this in core, I can PR to you. it's merely a helper loop function of a series of .get
calls and breaking on the first truthy value (although it currently checks for FALSE, but will skip undefined
and null
)
I would expect that
var o = { a: 'asdf' };
objectPath.ensureExists(o, 'a.b', 'bsdf');
console.log(o);
would result in {a: {b: 'bsdf'} }
but the result is {a: 'asdf' }
. This doesn't seem to work with objectPath.set()
either. Is this intended behavior or is there another way I could make this work without having to check objectPath.has(o, 'a.b')
?
Please consider implementing a concat
method so that multiple elements can be added to an array at once without a loop.
First of all, stoked you wrote this - was about to have to write it myself. Any chance of adding .delete(obj, path)
support? If you're too busy, let me know and I'll fork and PR it when I get a chance.
๐
Object-path's get
, set
, delete
and push
works well with array, but I can't insert something into the middle of array.
How about implementing the splice
?
After reading your code, I have one confusion about the implementation of getKey
function getKey(key){ var intKey = parseInt(key); if (intKey.toString() === key) { return intKey; } return key; }
Can someone explain a bit ? I mean I know what this function does. But why we have to use getKey to assign the value to something like currentPath as below
var currentPath = getKey(path[0]);
I am new to this library , i just want check if we can get the object-path based on a value i provide ?
var obj = {
a: {
b: "d",
c: ["e", "f"],
'\u1200': 'unicode key',
'dot.dot': 'key'
},
"x.y":"doesn't work"
};
objectPath.get(obj, ["x.y"]);
"doesn't work"
objectPath.get(obj, "x.y"); // This one doesn't work, and I would think it should :(
objectPath.get(obj, "a.dot.dot"); // This one doesn't work, and I would think it should :(
Can't see any reason why it wouldn't fallback to finding a['dot.dot'] if a.dot.dot is not defined. Am I missing something?
:)
objectPath.get({'a.b': {b: {c: 'd'}}}, 'a\\.b.b.c').should.eql('d');
Nice package, but what I should do, to make it work with property names, that contains dots?
For example:
var objectPath = require("object-path");
var a = {};
objectPath.set(a, 'a\.b', 1);
// a now equals {a: {b: 1} }
But shouldn't it be equal { 'a.b': 1 }
?
Hey, I've noticed that has
throws an error when I try to check a nested path in a sparse array. I believe it should return false
in this case.
Example:
objectPath.has({ foo: [undefined, undefined, undefined] }, 'foo.0.bar')
Throws:
Uncaught TypeError: Cannot convert undefined or null to object(โฆ)
objectPath.has @ index.js:136
I need to emit an event when the object is malformed.
Now i'm using a specific default value and i test if this value and emit the event.
For the callback, it can be nice if the first the parameter is an error with the current attribute name undefined or a string of this attribute name.
I can do a PR if you need.
So, I was checking for a cache entry, and the cache keys are completly random, but I knew the id of the cache item, basically it was like this:
cache = {
activities: {
'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad': [
{'id': 1 /*...*/},
{'id': 9 /*...*/},
/* ...etc */
]
}
};
I could just iterate through all activities, and check each one of the ids, but then I realized that I could use an 'exists' function that could 'ignore' parts of the path (using a *
and check for the existance of the deep path).
So, for that cache I'd do objectPath.exists(cache, 'activities.*.*.id', 1)
the last value is a comparision. If omitted, it just check of the path !== undefined. It's a nice addition to the library, what do you think @mariocasciaro ?
it's me again ๐ฑ
interested in adding a "empty" method, that empties the given path, like objectPath.empty(obj,'some.path')
.length = 0;
to itHi,
Can I use this utility to get the property itself rather than value?
For instance, I have a string path to a property, I want to delete that property from an object, so using this utility to find the property by string path, I get only its value and not a reference to it to use it later.
Any idea?
Thanks
Hi,
I think it would be really nice to detect if a property is a function
and return it's result.
It could be handy when you want get a value out of a getter.
If you still want to get the function itself, it could be specified via an optional parameter to get
.
You could also create another method
that does this, like fetch
or getOrCall
.
What do you think?
Thanks!
See https://runkit.com/586945e631af31001472d7f4/5871ebc5fe814b0013db83e5
Current behavior:
return the whole object.
Expected behavior:
return the defaultValue.
Related code:
Lines 224 to 229 in 2b80d6c
objectPath.move(obj, 'old.path', 'new.path');
instead of:
const path = objectPath.get(obj, 'old.path');
objectPath.set(obj, 'new.path', path);
objectPath.del(obj, 'old.path');
I'm inclined to throw an error (or an specialized error, like ObjectPathError
) when the provided path is empty. It's usually a programming mistake, and it bit me a couple of times (usually when dealing with JSON coming from the server). Returning the original object shouldn't be the right thing to do, since it can be 'truthy'.
Examples:
var ok;
Ok = 'some.path'; // mistyped the variable name
objectPath.set(obj, ok, 'some value');
/* ... */
$http.get('/endpoint').then(function(r) {
if (objectPath.has(obj, r.path)) { // returns true even though r.path is undefined!
return 'do something';
}
// it's actually in r.data.path, a return from angular $http, really easy to mess up
});
What is your opinion @mariocasciaro?
I think it's the only place where the module should actually forcefully throw, to prevent hard-to-debug scenarios, since it will 'swallow' the empty path. And we shouldn't worry about it in the next version, since it's a major breaking change anyway
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.