Safely setting object properties with dot notation strings in JavaScript
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
6
down vote
favorite
What my function does:
Safely sets object properties with dot notation strings in JavaScript.
That is to say, it allows setting a nested property on an object using a string such as "a.b.c".
For example: set(obj, "a.b.c", value)
would be equivalent to a.b.c = value
Some notes on intended behavior
- When setting a property at a given path, if any portion of that path doesn't exist, it should be created
- When creating a path portion, if the next key in the path is NOT an integer, the current portion should be created as an object
- When creating a path portion, if the next key in the path is an integer, the current portion should be created as an array
Why?
I'd like to be able to set deeply nested properties on an object in an environment where I can't be sure that all the properties in the path will always exist and I don't want to deal with the logic of checking for and setting each key manually in more than one place. This allows me to quickly set the needed value without caring if the keys were present previously or not.
/**
* Set the value for the given object for the given path
* where the path can be a nested key represented with dot notation
*
* @param object obj The object on which to set the given value
* @param string path The dot notation path to the nested property where the value should be set
* @param mixed value The value that should be set
* @return mixed
*
*/
function set(obj, path, value)
// protect against being something unexpected
obj = typeof obj === 'object' ? obj : ;
// split the path into and array if its not one already
var keys = Array.isArray(path) ? path : path.split('.');
// keep up with our current place in the object
// starting at the root object and drilling down
var curStep = obj;
// loop over the path parts one at a time
// but, dont iterate the last part,
for (var i = 0; i < keys.length - 1; i++)
// get the current path part
var key = keys[i];
// if nothing exists for this key, make it an empty object or array
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key))[1-9]d*)$/.test(nextKey);
curStep[key] = useArray ? : ;
// update curStep to point to the new level
curStep = curStep[key];
// set the final key to our value
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
;
/** Usage **/
console.log('setting non existant a.b.c to "some value":');
var test = ;
set(test, 'a.b.c', 'some value');
console.log(test);
console.log('updating a.b.c to "some new value":');
set(test, 'a.b.c', 'some new value');
console.log(test);
console.log('setting non existant a.b.0 to "some value":');
var test = ;
set(test, 'a.b.0', 'some value');
console.log(test);
console.log('updating a.b.0 to "some new value":');
set(test, 'a.b.0', 'some new value');
console.log(test);
javascript
add a comment |Â
up vote
6
down vote
favorite
What my function does:
Safely sets object properties with dot notation strings in JavaScript.
That is to say, it allows setting a nested property on an object using a string such as "a.b.c".
For example: set(obj, "a.b.c", value)
would be equivalent to a.b.c = value
Some notes on intended behavior
- When setting a property at a given path, if any portion of that path doesn't exist, it should be created
- When creating a path portion, if the next key in the path is NOT an integer, the current portion should be created as an object
- When creating a path portion, if the next key in the path is an integer, the current portion should be created as an array
Why?
I'd like to be able to set deeply nested properties on an object in an environment where I can't be sure that all the properties in the path will always exist and I don't want to deal with the logic of checking for and setting each key manually in more than one place. This allows me to quickly set the needed value without caring if the keys were present previously or not.
/**
* Set the value for the given object for the given path
* where the path can be a nested key represented with dot notation
*
* @param object obj The object on which to set the given value
* @param string path The dot notation path to the nested property where the value should be set
* @param mixed value The value that should be set
* @return mixed
*
*/
function set(obj, path, value)
// protect against being something unexpected
obj = typeof obj === 'object' ? obj : ;
// split the path into and array if its not one already
var keys = Array.isArray(path) ? path : path.split('.');
// keep up with our current place in the object
// starting at the root object and drilling down
var curStep = obj;
// loop over the path parts one at a time
// but, dont iterate the last part,
for (var i = 0; i < keys.length - 1; i++)
// get the current path part
var key = keys[i];
// if nothing exists for this key, make it an empty object or array
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key))[1-9]d*)$/.test(nextKey);
curStep[key] = useArray ? : ;
// update curStep to point to the new level
curStep = curStep[key];
// set the final key to our value
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
;
/** Usage **/
console.log('setting non existant a.b.c to "some value":');
var test = ;
set(test, 'a.b.c', 'some value');
console.log(test);
console.log('updating a.b.c to "some new value":');
set(test, 'a.b.c', 'some new value');
console.log(test);
console.log('setting non existant a.b.0 to "some value":');
var test = ;
set(test, 'a.b.0', 'some value');
console.log(test);
console.log('updating a.b.0 to "some new value":');
set(test, 'a.b.0', 'some new value');
console.log(test);
javascript
1
Just in case you're not aware of it, Dottie may have considered some of the issues raised below - github.com/mickhansen/dottie.js
â iabw
May 14 at 15:03
add a comment |Â
up vote
6
down vote
favorite
up vote
6
down vote
favorite
What my function does:
Safely sets object properties with dot notation strings in JavaScript.
That is to say, it allows setting a nested property on an object using a string such as "a.b.c".
For example: set(obj, "a.b.c", value)
would be equivalent to a.b.c = value
Some notes on intended behavior
- When setting a property at a given path, if any portion of that path doesn't exist, it should be created
- When creating a path portion, if the next key in the path is NOT an integer, the current portion should be created as an object
- When creating a path portion, if the next key in the path is an integer, the current portion should be created as an array
Why?
I'd like to be able to set deeply nested properties on an object in an environment where I can't be sure that all the properties in the path will always exist and I don't want to deal with the logic of checking for and setting each key manually in more than one place. This allows me to quickly set the needed value without caring if the keys were present previously or not.
/**
* Set the value for the given object for the given path
* where the path can be a nested key represented with dot notation
*
* @param object obj The object on which to set the given value
* @param string path The dot notation path to the nested property where the value should be set
* @param mixed value The value that should be set
* @return mixed
*
*/
function set(obj, path, value)
// protect against being something unexpected
obj = typeof obj === 'object' ? obj : ;
// split the path into and array if its not one already
var keys = Array.isArray(path) ? path : path.split('.');
// keep up with our current place in the object
// starting at the root object and drilling down
var curStep = obj;
// loop over the path parts one at a time
// but, dont iterate the last part,
for (var i = 0; i < keys.length - 1; i++)
// get the current path part
var key = keys[i];
// if nothing exists for this key, make it an empty object or array
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key))[1-9]d*)$/.test(nextKey);
curStep[key] = useArray ? : ;
// update curStep to point to the new level
curStep = curStep[key];
// set the final key to our value
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
;
/** Usage **/
console.log('setting non existant a.b.c to "some value":');
var test = ;
set(test, 'a.b.c', 'some value');
console.log(test);
console.log('updating a.b.c to "some new value":');
set(test, 'a.b.c', 'some new value');
console.log(test);
console.log('setting non existant a.b.0 to "some value":');
var test = ;
set(test, 'a.b.0', 'some value');
console.log(test);
console.log('updating a.b.0 to "some new value":');
set(test, 'a.b.0', 'some new value');
console.log(test);
javascript
What my function does:
Safely sets object properties with dot notation strings in JavaScript.
That is to say, it allows setting a nested property on an object using a string such as "a.b.c".
For example: set(obj, "a.b.c", value)
would be equivalent to a.b.c = value
Some notes on intended behavior
- When setting a property at a given path, if any portion of that path doesn't exist, it should be created
- When creating a path portion, if the next key in the path is NOT an integer, the current portion should be created as an object
- When creating a path portion, if the next key in the path is an integer, the current portion should be created as an array
Why?
I'd like to be able to set deeply nested properties on an object in an environment where I can't be sure that all the properties in the path will always exist and I don't want to deal with the logic of checking for and setting each key manually in more than one place. This allows me to quickly set the needed value without caring if the keys were present previously or not.
/**
* Set the value for the given object for the given path
* where the path can be a nested key represented with dot notation
*
* @param object obj The object on which to set the given value
* @param string path The dot notation path to the nested property where the value should be set
* @param mixed value The value that should be set
* @return mixed
*
*/
function set(obj, path, value)
// protect against being something unexpected
obj = typeof obj === 'object' ? obj : ;
// split the path into and array if its not one already
var keys = Array.isArray(path) ? path : path.split('.');
// keep up with our current place in the object
// starting at the root object and drilling down
var curStep = obj;
// loop over the path parts one at a time
// but, dont iterate the last part,
for (var i = 0; i < keys.length - 1; i++)
// get the current path part
var key = keys[i];
// if nothing exists for this key, make it an empty object or array
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key))[1-9]d*)$/.test(nextKey);
curStep[key] = useArray ? : ;
// update curStep to point to the new level
curStep = curStep[key];
// set the final key to our value
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
;
/** Usage **/
console.log('setting non existant a.b.c to "some value":');
var test = ;
set(test, 'a.b.c', 'some value');
console.log(test);
console.log('updating a.b.c to "some new value":');
set(test, 'a.b.c', 'some new value');
console.log(test);
console.log('setting non existant a.b.0 to "some value":');
var test = ;
set(test, 'a.b.0', 'some value');
console.log(test);
console.log('updating a.b.0 to "some new value":');
set(test, 'a.b.0', 'some new value');
console.log(test);
/**
* Set the value for the given object for the given path
* where the path can be a nested key represented with dot notation
*
* @param object obj The object on which to set the given value
* @param string path The dot notation path to the nested property where the value should be set
* @param mixed value The value that should be set
* @return mixed
*
*/
function set(obj, path, value)
// protect against being something unexpected
obj = typeof obj === 'object' ? obj : ;
// split the path into and array if its not one already
var keys = Array.isArray(path) ? path : path.split('.');
// keep up with our current place in the object
// starting at the root object and drilling down
var curStep = obj;
// loop over the path parts one at a time
// but, dont iterate the last part,
for (var i = 0; i < keys.length - 1; i++)
// get the current path part
var key = keys[i];
// if nothing exists for this key, make it an empty object or array
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key))[1-9]d*)$/.test(nextKey);
curStep[key] = useArray ? : ;
// update curStep to point to the new level
curStep = curStep[key];
// set the final key to our value
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
;
/** Usage **/
console.log('setting non existant a.b.c to "some value":');
var test = ;
set(test, 'a.b.c', 'some value');
console.log(test);
console.log('updating a.b.c to "some new value":');
set(test, 'a.b.c', 'some new value');
console.log(test);
console.log('setting non existant a.b.0 to "some value":');
var test = ;
set(test, 'a.b.0', 'some value');
console.log(test);
console.log('updating a.b.0 to "some new value":');
set(test, 'a.b.0', 'some new value');
console.log(test);
/**
* Set the value for the given object for the given path
* where the path can be a nested key represented with dot notation
*
* @param object obj The object on which to set the given value
* @param string path The dot notation path to the nested property where the value should be set
* @param mixed value The value that should be set
* @return mixed
*
*/
function set(obj, path, value)
// protect against being something unexpected
obj = typeof obj === 'object' ? obj : ;
// split the path into and array if its not one already
var keys = Array.isArray(path) ? path : path.split('.');
// keep up with our current place in the object
// starting at the root object and drilling down
var curStep = obj;
// loop over the path parts one at a time
// but, dont iterate the last part,
for (var i = 0; i < keys.length - 1; i++)
// get the current path part
var key = keys[i];
// if nothing exists for this key, make it an empty object or array
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key))[1-9]d*)$/.test(nextKey);
curStep[key] = useArray ? : ;
// update curStep to point to the new level
curStep = curStep[key];
// set the final key to our value
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
;
/** Usage **/
console.log('setting non existant a.b.c to "some value":');
var test = ;
set(test, 'a.b.c', 'some value');
console.log(test);
console.log('updating a.b.c to "some new value":');
set(test, 'a.b.c', 'some new value');
console.log(test);
console.log('setting non existant a.b.0 to "some value":');
var test = ;
set(test, 'a.b.0', 'some value');
console.log(test);
console.log('updating a.b.0 to "some new value":');
set(test, 'a.b.0', 'some new value');
console.log(test);
javascript
asked May 14 at 3:35
DelightedD0D
1607
1607
1
Just in case you're not aware of it, Dottie may have considered some of the issues raised below - github.com/mickhansen/dottie.js
â iabw
May 14 at 15:03
add a comment |Â
1
Just in case you're not aware of it, Dottie may have considered some of the issues raised below - github.com/mickhansen/dottie.js
â iabw
May 14 at 15:03
1
1
Just in case you're not aware of it, Dottie may have considered some of the issues raised below - github.com/mickhansen/dottie.js
â iabw
May 14 at 15:03
Just in case you're not aware of it, Dottie may have considered some of the issues raised below - github.com/mickhansen/dottie.js
â iabw
May 14 at 15:03
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
5
down vote
accepted
Dangerous
There are so many caveats in this function I would really not allow it in any code base because of potencial unexpected , or misunderstood behaviour. Its just waiting to cause problems in completely unrelated code.
Some points.
Some variables should be
const
they arekeys
,key
,useArray
,nextKey
,finalStep
Don't see why you create a new variable
curStep
(with very odd name) to hold the current object. Just reuseobj
Avoid the name
set
as it is used as a JavaScript token eg create a setterset val(blah) ...
Maybe the name could beassignToPath
null
is also of type"object"
so it will pay to extend the test on the first line to include thenull
Would be best to throwYou currently return
undefined
. Maybe it would be more helpful to return the object you assigned the new property to.Why use the long version
!Object.prototype.hasOwnProperty.call(curStep, key)
?? when!curStep.hasOwnProperty(key)
will do the same.If a property is expressly set to
undefined
your function fails.
const test = a: undefined
assignToPath(test, "a.b", "foo" ); //throws but would expect test.a.b = "foo"
Objects can be locked via various
Object
functionsObject.freeze
,Object.seal
,Object.preventExtensions
,Object.defineProperties
andObject.defineProperty
Your function ignores these settings.If a property exists but is not an
Object
orArray
you still attempt to add a property to it.
const test = a: "blah"
assignToPath(test, "a.b", "foo" ); // fails
- This is what I would consider a low level function and as such would be one of the few that need to throw errors rather than fail silently. It should throw at anything that is not as expected. For example if the object to assign a property to is an array, or an object
const test = a:
assignToPath(test, "a.b", "foo" ); // Maybe this should throw
const test = a:
assignToPath(test, "a.1", "foo" ); // This should throw as it is unclear what the result should be.
// the case against the above
const test =
assignToPath(test, "a.1", "foo" ); // creates an array
Suggestions only.
Adding a settings argument would allow for better control of the behaviour when things get a little ambiguous.
assignToPath(a:, "a.b", "bar", arrayHaveProps : true);
assignToPath(a:, "a.1", "bar", allowObjIndex : true);
You can shorten the code if you use a while loop and shift the path as you go.
// simple example of using `while` and `shift` rather than a `for` loop
var obj = a:b:c:"a";
const path = "a.b.c".split(".");
while(path.length > 1)
obj = obj[path.shift()];
obj[path.shift()] = "b";
I guessset(, "hasOwnProperty", null)
is a reason to useObject.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
or evenObject.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
@sineemore Your examples are not reason to use the indirect call. How aboutObject.prototype.hasOwnProperty =()=>
Now what???
â Blindman67
May 14 at 9:13
add a comment |Â
up vote
7
down vote
obj = typeof obj === 'object' ? obj : ;
When obj
argument points to something other than object
you will create one. Like in set("foo", "a.b.c", "Yay!")
. I guess you want to return it at function end or it will be lost.
Other notes:
/^+?(0|[1-9]d*)$/
allows+
in integer key inputs- you may use
isNaN(+nextKey)
instead of regular expression, but beware float or signed numbers finalStep
is a bit misleading name. You may usekey = keys[keys.length - 1]
instead.
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
5
down vote
accepted
Dangerous
There are so many caveats in this function I would really not allow it in any code base because of potencial unexpected , or misunderstood behaviour. Its just waiting to cause problems in completely unrelated code.
Some points.
Some variables should be
const
they arekeys
,key
,useArray
,nextKey
,finalStep
Don't see why you create a new variable
curStep
(with very odd name) to hold the current object. Just reuseobj
Avoid the name
set
as it is used as a JavaScript token eg create a setterset val(blah) ...
Maybe the name could beassignToPath
null
is also of type"object"
so it will pay to extend the test on the first line to include thenull
Would be best to throwYou currently return
undefined
. Maybe it would be more helpful to return the object you assigned the new property to.Why use the long version
!Object.prototype.hasOwnProperty.call(curStep, key)
?? when!curStep.hasOwnProperty(key)
will do the same.If a property is expressly set to
undefined
your function fails.
const test = a: undefined
assignToPath(test, "a.b", "foo" ); //throws but would expect test.a.b = "foo"
Objects can be locked via various
Object
functionsObject.freeze
,Object.seal
,Object.preventExtensions
,Object.defineProperties
andObject.defineProperty
Your function ignores these settings.If a property exists but is not an
Object
orArray
you still attempt to add a property to it.
const test = a: "blah"
assignToPath(test, "a.b", "foo" ); // fails
- This is what I would consider a low level function and as such would be one of the few that need to throw errors rather than fail silently. It should throw at anything that is not as expected. For example if the object to assign a property to is an array, or an object
const test = a:
assignToPath(test, "a.b", "foo" ); // Maybe this should throw
const test = a:
assignToPath(test, "a.1", "foo" ); // This should throw as it is unclear what the result should be.
// the case against the above
const test =
assignToPath(test, "a.1", "foo" ); // creates an array
Suggestions only.
Adding a settings argument would allow for better control of the behaviour when things get a little ambiguous.
assignToPath(a:, "a.b", "bar", arrayHaveProps : true);
assignToPath(a:, "a.1", "bar", allowObjIndex : true);
You can shorten the code if you use a while loop and shift the path as you go.
// simple example of using `while` and `shift` rather than a `for` loop
var obj = a:b:c:"a";
const path = "a.b.c".split(".");
while(path.length > 1)
obj = obj[path.shift()];
obj[path.shift()] = "b";
I guessset(, "hasOwnProperty", null)
is a reason to useObject.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
or evenObject.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
@sineemore Your examples are not reason to use the indirect call. How aboutObject.prototype.hasOwnProperty =()=>
Now what???
â Blindman67
May 14 at 9:13
add a comment |Â
up vote
5
down vote
accepted
Dangerous
There are so many caveats in this function I would really not allow it in any code base because of potencial unexpected , or misunderstood behaviour. Its just waiting to cause problems in completely unrelated code.
Some points.
Some variables should be
const
they arekeys
,key
,useArray
,nextKey
,finalStep
Don't see why you create a new variable
curStep
(with very odd name) to hold the current object. Just reuseobj
Avoid the name
set
as it is used as a JavaScript token eg create a setterset val(blah) ...
Maybe the name could beassignToPath
null
is also of type"object"
so it will pay to extend the test on the first line to include thenull
Would be best to throwYou currently return
undefined
. Maybe it would be more helpful to return the object you assigned the new property to.Why use the long version
!Object.prototype.hasOwnProperty.call(curStep, key)
?? when!curStep.hasOwnProperty(key)
will do the same.If a property is expressly set to
undefined
your function fails.
const test = a: undefined
assignToPath(test, "a.b", "foo" ); //throws but would expect test.a.b = "foo"
Objects can be locked via various
Object
functionsObject.freeze
,Object.seal
,Object.preventExtensions
,Object.defineProperties
andObject.defineProperty
Your function ignores these settings.If a property exists but is not an
Object
orArray
you still attempt to add a property to it.
const test = a: "blah"
assignToPath(test, "a.b", "foo" ); // fails
- This is what I would consider a low level function and as such would be one of the few that need to throw errors rather than fail silently. It should throw at anything that is not as expected. For example if the object to assign a property to is an array, or an object
const test = a:
assignToPath(test, "a.b", "foo" ); // Maybe this should throw
const test = a:
assignToPath(test, "a.1", "foo" ); // This should throw as it is unclear what the result should be.
// the case against the above
const test =
assignToPath(test, "a.1", "foo" ); // creates an array
Suggestions only.
Adding a settings argument would allow for better control of the behaviour when things get a little ambiguous.
assignToPath(a:, "a.b", "bar", arrayHaveProps : true);
assignToPath(a:, "a.1", "bar", allowObjIndex : true);
You can shorten the code if you use a while loop and shift the path as you go.
// simple example of using `while` and `shift` rather than a `for` loop
var obj = a:b:c:"a";
const path = "a.b.c".split(".");
while(path.length > 1)
obj = obj[path.shift()];
obj[path.shift()] = "b";
I guessset(, "hasOwnProperty", null)
is a reason to useObject.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
or evenObject.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
@sineemore Your examples are not reason to use the indirect call. How aboutObject.prototype.hasOwnProperty =()=>
Now what???
â Blindman67
May 14 at 9:13
add a comment |Â
up vote
5
down vote
accepted
up vote
5
down vote
accepted
Dangerous
There are so many caveats in this function I would really not allow it in any code base because of potencial unexpected , or misunderstood behaviour. Its just waiting to cause problems in completely unrelated code.
Some points.
Some variables should be
const
they arekeys
,key
,useArray
,nextKey
,finalStep
Don't see why you create a new variable
curStep
(with very odd name) to hold the current object. Just reuseobj
Avoid the name
set
as it is used as a JavaScript token eg create a setterset val(blah) ...
Maybe the name could beassignToPath
null
is also of type"object"
so it will pay to extend the test on the first line to include thenull
Would be best to throwYou currently return
undefined
. Maybe it would be more helpful to return the object you assigned the new property to.Why use the long version
!Object.prototype.hasOwnProperty.call(curStep, key)
?? when!curStep.hasOwnProperty(key)
will do the same.If a property is expressly set to
undefined
your function fails.
const test = a: undefined
assignToPath(test, "a.b", "foo" ); //throws but would expect test.a.b = "foo"
Objects can be locked via various
Object
functionsObject.freeze
,Object.seal
,Object.preventExtensions
,Object.defineProperties
andObject.defineProperty
Your function ignores these settings.If a property exists but is not an
Object
orArray
you still attempt to add a property to it.
const test = a: "blah"
assignToPath(test, "a.b", "foo" ); // fails
- This is what I would consider a low level function and as such would be one of the few that need to throw errors rather than fail silently. It should throw at anything that is not as expected. For example if the object to assign a property to is an array, or an object
const test = a:
assignToPath(test, "a.b", "foo" ); // Maybe this should throw
const test = a:
assignToPath(test, "a.1", "foo" ); // This should throw as it is unclear what the result should be.
// the case against the above
const test =
assignToPath(test, "a.1", "foo" ); // creates an array
Suggestions only.
Adding a settings argument would allow for better control of the behaviour when things get a little ambiguous.
assignToPath(a:, "a.b", "bar", arrayHaveProps : true);
assignToPath(a:, "a.1", "bar", allowObjIndex : true);
You can shorten the code if you use a while loop and shift the path as you go.
// simple example of using `while` and `shift` rather than a `for` loop
var obj = a:b:c:"a";
const path = "a.b.c".split(".");
while(path.length > 1)
obj = obj[path.shift()];
obj[path.shift()] = "b";
Dangerous
There are so many caveats in this function I would really not allow it in any code base because of potencial unexpected , or misunderstood behaviour. Its just waiting to cause problems in completely unrelated code.
Some points.
Some variables should be
const
they arekeys
,key
,useArray
,nextKey
,finalStep
Don't see why you create a new variable
curStep
(with very odd name) to hold the current object. Just reuseobj
Avoid the name
set
as it is used as a JavaScript token eg create a setterset val(blah) ...
Maybe the name could beassignToPath
null
is also of type"object"
so it will pay to extend the test on the first line to include thenull
Would be best to throwYou currently return
undefined
. Maybe it would be more helpful to return the object you assigned the new property to.Why use the long version
!Object.prototype.hasOwnProperty.call(curStep, key)
?? when!curStep.hasOwnProperty(key)
will do the same.If a property is expressly set to
undefined
your function fails.
const test = a: undefined
assignToPath(test, "a.b", "foo" ); //throws but would expect test.a.b = "foo"
Objects can be locked via various
Object
functionsObject.freeze
,Object.seal
,Object.preventExtensions
,Object.defineProperties
andObject.defineProperty
Your function ignores these settings.If a property exists but is not an
Object
orArray
you still attempt to add a property to it.
const test = a: "blah"
assignToPath(test, "a.b", "foo" ); // fails
- This is what I would consider a low level function and as such would be one of the few that need to throw errors rather than fail silently. It should throw at anything that is not as expected. For example if the object to assign a property to is an array, or an object
const test = a:
assignToPath(test, "a.b", "foo" ); // Maybe this should throw
const test = a:
assignToPath(test, "a.1", "foo" ); // This should throw as it is unclear what the result should be.
// the case against the above
const test =
assignToPath(test, "a.1", "foo" ); // creates an array
Suggestions only.
Adding a settings argument would allow for better control of the behaviour when things get a little ambiguous.
assignToPath(a:, "a.b", "bar", arrayHaveProps : true);
assignToPath(a:, "a.1", "bar", allowObjIndex : true);
You can shorten the code if you use a while loop and shift the path as you go.
// simple example of using `while` and `shift` rather than a `for` loop
var obj = a:b:c:"a";
const path = "a.b.c".split(".");
while(path.length > 1)
obj = obj[path.shift()];
obj[path.shift()] = "b";
answered May 14 at 7:31
Blindman67
5,2961320
5,2961320
I guessset(, "hasOwnProperty", null)
is a reason to useObject.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
or evenObject.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
@sineemore Your examples are not reason to use the indirect call. How aboutObject.prototype.hasOwnProperty =()=>
Now what???
â Blindman67
May 14 at 9:13
add a comment |Â
I guessset(, "hasOwnProperty", null)
is a reason to useObject.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
or evenObject.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
@sineemore Your examples are not reason to use the indirect call. How aboutObject.prototype.hasOwnProperty =()=>
Now what???
â Blindman67
May 14 at 9:13
I guess
set(, "hasOwnProperty", null)
is a reason to use Object.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
I guess
set(, "hasOwnProperty", null)
is a reason to use Object.prototype.hasOwnProperty
â sineemore
May 14 at 8:00
or even
Object.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
or even
Object.create(null).hasOwnProperty()
â sineemore
May 14 at 8:02
@sineemore Your examples are not reason to use the indirect call. How about
Object.prototype.hasOwnProperty =()=>
Now what???â Blindman67
May 14 at 9:13
@sineemore Your examples are not reason to use the indirect call. How about
Object.prototype.hasOwnProperty =()=>
Now what???â Blindman67
May 14 at 9:13
add a comment |Â
up vote
7
down vote
obj = typeof obj === 'object' ? obj : ;
When obj
argument points to something other than object
you will create one. Like in set("foo", "a.b.c", "Yay!")
. I guess you want to return it at function end or it will be lost.
Other notes:
/^+?(0|[1-9]d*)$/
allows+
in integer key inputs- you may use
isNaN(+nextKey)
instead of regular expression, but beware float or signed numbers finalStep
is a bit misleading name. You may usekey = keys[keys.length - 1]
instead.
add a comment |Â
up vote
7
down vote
obj = typeof obj === 'object' ? obj : ;
When obj
argument points to something other than object
you will create one. Like in set("foo", "a.b.c", "Yay!")
. I guess you want to return it at function end or it will be lost.
Other notes:
/^+?(0|[1-9]d*)$/
allows+
in integer key inputs- you may use
isNaN(+nextKey)
instead of regular expression, but beware float or signed numbers finalStep
is a bit misleading name. You may usekey = keys[keys.length - 1]
instead.
add a comment |Â
up vote
7
down vote
up vote
7
down vote
obj = typeof obj === 'object' ? obj : ;
When obj
argument points to something other than object
you will create one. Like in set("foo", "a.b.c", "Yay!")
. I guess you want to return it at function end or it will be lost.
Other notes:
/^+?(0|[1-9]d*)$/
allows+
in integer key inputs- you may use
isNaN(+nextKey)
instead of regular expression, but beware float or signed numbers finalStep
is a bit misleading name. You may usekey = keys[keys.length - 1]
instead.
obj = typeof obj === 'object' ? obj : ;
When obj
argument points to something other than object
you will create one. Like in set("foo", "a.b.c", "Yay!")
. I guess you want to return it at function end or it will be lost.
Other notes:
/^+?(0|[1-9]d*)$/
allows+
in integer key inputs- you may use
isNaN(+nextKey)
instead of regular expression, but beware float or signed numbers finalStep
is a bit misleading name. You may usekey = keys[keys.length - 1]
instead.
answered May 14 at 7:47
sineemore
1,193217
1,193217
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194338%2fsafely-setting-object-properties-with-dot-notation-strings-in-javascript%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
Just in case you're not aware of it, Dottie may have considered some of the issues raised below - github.com/mickhansen/dottie.js
â iabw
May 14 at 15:03