In javascript, I am trying to parse a given string that contains brackets, indicating object properties are involved in getting the value from an object.
The object property is someobj.pform.options.foo = "somestring";
Using javascript/jQuery, how can I accomplish this? The problem I have with the method I've devised (below) does not reassign the object within the loop.
var uiobject = {};
uiobject.name = "options[foo]";
var objectvalue = "";
var props = uiobject.name.replace(/\]/g, '').split('[');
var temp = someobj.pform;
for(var z=0;z<props.length;z++) {
if(temp.hasOwnProperty[ (props[z]) ]) {
if(z == (props.length - 1)) {
objectvalue = temp[ props[z] ];
} else {
temp = temp[ (props[z]) ];
}
}
}
console.log("value: "+objectvalue);
It seems like you might be misunderstanding hasOwnProperty. If you negate the predicate (or remove that block completely), it works:
var uiobject = {};
uiobject.name = "options[foo][bar]";
var objectvalue = "";
var props = uiobject.name.replace(/\]/g, '').split('[');
var temp = {options:{foo:{bar:"baz"}}};
for(var z=0;z<props.length;z++) {
if(!temp.hasOwnProperty[ (props[z]) ]) {
if(z == (props.length - 1)) {
objectvalue = temp[ props[z] ];
} else {
temp = temp[ (props[z]) ];
}
}
}
console.log("value: "+objectvalue);
Related
I'm starting with unit testing. I need to create some fake data to run the tests. So let's say inside a stubbed method I'm passing an obj as an argument and I do things with obj.obj1.obj2.data inside the function. Is there a way to set this fake object? So, given:
obj.obj1.obj2.data
It creates:
obj = {
obj1: {
obj2: {
data: 'whatever'}}}
So it would be at the end something like:
var obj = creator('obj.obj1.obj2.data', 20);
Assuming the string is only a set of objects (no arrays) this should be fairly straightforward. Just split the input string on . and then use a while loop to do the nesting.
function creator(str,val){
var tree = str.split('.');
var ret = {};
var cur = ret;
while(tree.length){
var name = tree.shift();
cur[name] = tree.length ? {} : val;
cur = cur[name];
}
return ret;
}
document.querySelector("#out").innerHTML = JSON.stringify(creator('obj.obj1.obj2.data',20));
<div id="out"></div>
Just in case anyone else in interested, I created a simple npm module with the function below (https://github.com/r01010010/zappy) check it out:
var objFrom = function(str, last_value){
var objs = str.split('.');
var r = {};
var last = r;
for(i=0; i < objs.length; i++) {
if(i !== objs.length - 1){
last = last[objs[i]] = {};
}else{
last[objs[i]] = last_value;
}
}
return r;
}
var obj = objFrom('obj1.obj2.data', 20);
console.log(obj.obj1.obj2.data);
I am trying to achieve something which seemed very basic but is getting me mad over the last days.
I have a simple array : ["a","b","c","d","e"] and I want to turn it into a nested JSON like this:
{"a":{"b":{"c":{"d":{"e":""}}}}}
Looping over it, I ran in problems like "how do you save the last key to set it afterwards without erasing it" and so on.
Does anyone has an idea?
You might have had problems because you were looping in the wrong direction. Try to build the object from inside-out:
array.reduceRight(function(v, key) {
var o = {};
o[key] = v;
return o;
}, "")
or, with a loop:
var val = "";
for (var i=array.length; i--; )
var o = {};
o[array[i]] = val;
val = o;
}
return val;
Here's one way to do it, recursively:
function convertToNestedObject(arr) {
var result = {};
if (arr.length === 1) {
result[arr[0]] = '';
} else {
result[arr[0]] = convertToNestedObject(arr.slice(1, arr.length));
}
return result;
}
You could pass the start index in to the function instead of using slice and creating copies of the array:
function convertToNestedObject(arr, startIndex) {
var result = {};
if (arr.length - startIndex === 1) {
result[arr[startIndex]] = '';
} else {
result[arr[startIndex]] = convertToNestedObject(arr, startIndex + 1);
}
return result;
}
Example: http://jsfiddle.net/jwcxfaeb/1/
Put current element as key and empty object ({}) as value. Continue with newly inserted empty object.
function toNested(arr){
var nested = {};
var temp = nested;
for(var i=0; i<arr.length; i++){
temp[arr[i]] = {};
temp = temp[arr[i]];
}
return nested;
}
I am trying to create a javascript object like
var allUserExpiry={};
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
But I am getting an error like allUserExpiry[aData.userId] undefined.
Is there a way, whereby I can set multi-level JS-Object keys? or is it important that I should go by doing allUserExpiry[aData.userId]={}, then allUserExpiry[aData.userId][aData.courseId]={} ?
Please let me know if there are any utility functions available for the same.
No, there is no way to set "multilevel keys". You need to initialize each object before trying to add properties to it.
var allUserExpiry = {};
allUserExpiry[aData.userId] = {}
allUserExpiry[aData.userId][aData.courseId] = {}
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
Using Computed property names from ES6, it is possible to do:
var allUserExpiry = {
[aData.userId] = {
[aData.courseId]: {
[aData.uscId]: aData
}
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
Simply use loadash,
let object = {};
let property = "a.b.c";
let value = 1;
_.set(object, property, value); // sets property based on path
let value = _.get(object, property, default); // gets property based on path
Or you can do it:
function setByPath(obj, path, value) {
var parts = path.split('.');
var o = obj;
if (parts.length > 1) {
for (var i = 0; i < parts.length - 1; i++) {
if (!o[parts[i]])
o[parts[i]] = {};
o = o[parts[i]];
}
}
o[parts[parts.length - 1]] = value;
}
And use:
setByPath(obj, 'path.path2.path', someValue);
This approach has many weak places, but for fun... :)
Why not just do this?
var allUserExpiry={};
allUserExpiry[aData.userId] = {aData.courseId: {aData.uscId: aData}};
I have a pretty hacky but short way of doing it in IE9+ as well as real browsers.
Given var path = 'aaa.bbb.ccc.ddd.eee'; where path is what your intending to make into an object and var result = {}; will will create the object {aaa: {bbb: {ccc: {ddd: {eee: {}}}}}
result = {}
path.split('.').reduce(function(prev, e) {
var newObj = {};
prev[e] = newObj;
return newObj;
}, result);
will store the object in result.
How it works:
split('.') converts the input into ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
reduce(function (...) {...}, result) runs through the array created by split, and for each entry will pass along a returned value to the next one. In our case we pass the new object through after adding the new object to the old one. This creates a chain of objects. reduce returns the last object you return inside of it, so we have to defined result beforehand.
This relies on using references so it won't be immediately clear how it works if you're expecting your code to be maintained by anyone else and should probably be avoided to be honest, but it works at least.
You can also use the following to create the initial structure:
var x = function(obj, keys) {
if (!obj) return;
var i, t;
for (i = 0; i < keys.length; i++) {
if (!t) {
t = obj[keys[i]] = {};
} else {
t[keys[i]] = {};
t = t[keys[i]];
}
}
};
var a = {};
x(a, ['A', 'B', 'C', 'D', 'E', 'F']);
Another approach without strings or array as argument.
function fillObject() {
var o = arguments[0];
for(var i = 1; i < arguments.length-1; i++) {
if(!o.hasOwnProperty(arguments[i])) {
o[arguments[i]] = {};
}
if(i < arguments.length-2) {
o = o[arguments[i]];
}else {
o[arguments[i]] = arguments[i+1]
}
}
}
var myObj = {"foo":{}};
fillObject(myObj,"back","to","the","future",2);
console.log(JSON.stringify(myObj));
// {"foo":{},"back":{"to":{"the":{"future":2}}}}
But I wouldn't use it :-) It's just for fun.
Because I don't like too much intelligent algorithm. (If it was in this category)
Using lodash you can do this easily (node exists and empty check for that node)..
var lodash = require('lodash-contrib');
function invalidateRequest(obj, param) {
var valid = true;
param.forEach(function(val) {
if(!lodash.hasPath(obj, val)) {
valid = false;
} else {
if(lodash.getPath(obj, val) == null || lodash.getPath(obj, val) == undefined || lodash.getPath(obj, val) == '') {
valid = false;
}
}
});
return valid;
}
Usage:
leaveDetails = {
"startDay": 1414998000000,
"endDay": 1415084400000,
"test": { "test1" : 1234 }
};
var validate;
validate = invalidateRequest(leaveDetails, ['startDay', 'endDay', 'test.test1']);
it will return boolean.
Another solution using reduce function (thanks Brian K).
Here we created a get/set to general proposes. The first function return the value in any level. The key is splited considering the separator. the function return the value refered from last index in the key's array
The second function will set the new value considering the last index of the splited key
the code:
function getObjectMultiLevelValue(_array,key,separator){
key = key.split(separator || '.');
var _value = JSON.parse(JSON.stringify(_array));
for(var ki in key){
_value = _value[key[ki]];
}
return _value;
}
function setObjectMultiLevelValue(_array,key,value,forcemode,separator){
key.split(separator || '.').reduce(function(prev, currKey, currIndex,keysArr) {
var newObj = {};
if(prev[currKey] && !forcemode){
newObj = prev[currKey];
}
if(keysArr[keysArr.length-1] == currKey){
newObj = value;
prev[currKey] = newObj;
}
prev[currKey] = newObj;
return newObj;
}, _array);
return _array;
}
//testing the function
//creating an array
var _someArray = {a:'a',b:'b',c:{c1:'c1',c2:{c21:'nothing here...'}}};
//a multilevel key to test
var _key = 'a,a1,a21';
//any value
var _value = 'new foo in a21 key forcing replace old path';
//here the new value will be inserted even if the path exists (forcemode=true). Using comma separator
setObjectMultiLevelValue(_someArray,_key,_value,true,',');
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//inserting another value in another key... using default separator
_key = 'c.c2.c21';
_value = 'new foo in c21 key';
setObjectMultiLevelValue(_someArray,_key,_value);
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//recovering the saved value with different separators
_key = 'a,a1,a21';
console.log(getObjectMultiLevelValue(_someArray,_key,','));
_key = 'c.c2.c21';
console.log(getObjectMultiLevelValue(_someArray,_key));
Let assume our object is
const data = {
//some other data
userInfo: {},
};
First, define a new property of that object
data.userInfo.vehicle = {};
then simply
data.userInfo.vehicle.vehicleType = state.userInfo.vehicleType;
Say I have an object and I want to set a variable deep nested inside this object, but the var does not yet exist. What's the best way to do this? If I for example have a string that shows where the variable to be updated should be, like below.
var myObject = {};
var location = "myObject.test.myVar";
var value = "My value!";
setValue(location, value, myObject);
I want this to result in:
myObject = {
test: {
myVar: "My value!"
}
};
And location can be much deeper than that.
Any help is appreciated!
Thanks!
Andreas
This function will do what you want.
Note that it changes the object by reference.
Note it ignores the first name as it's the object itself.
function setValue(location, value, object)
{
var path = location.split(".");
var current = object;
for (var i = 1; i < path.length; i++)
{
if ((i + 1) == path.length)
current[path[i]] = value;
else
{
current[path[i]] = {};
current = current[path[i]];
}
}
}
var myObject = {};
var location = "test.my.deep.hidden.nested.myVar";
var otherLoc = "test.my.deep.secret.var";
var value = "My value!";
function setValue(location, value, obj){
var i, prev = obj, curr;
location = location.split(".");
for(i = 0; i < location.length - 1; ++i){
curr = prev[location[i]];
if("object" !== typeof curr){
prev[location[i]] = {}
curr = prev[location[i]];
}
prev = curr;
}
curr[location[i]] = value;
}
setValue(location, value, myObject);
setValue(otherLoc, 42, myObject);
console.log(JSON.stringify(myObject));
Result:
{
"test": {
"my": {
"deep": {
"hidden": {
"nested": {
"myVar": "My value!"
}
},
"secret": {
"var":42
}
}
}
}
}
Note that you might want to add some features, like checking whether the location is actually valid ("this.is..invalid").
You can simply access object inside another object by Dot(.)
So in your case, you are specifying the expression in quotes. You can try this out
var location = myObject.test.myVar; //Without quotes
and similarly if you have more nested objects.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Dynamic object property name
I want to dynamically generate access to an object's property.
If I try to access mydata[i].val.name I get somename.
If I try it like mydata[i] + bar[j] (where bar[j] === '.val.name') it fails.
How do I dynamically create something like this? So that I can access any property of an object using a user generated value?
Some code:
If I have an object I want to be able to iterate through its properties, gathering the ones I am interested in. Ideally I would like something like the following:
var processData = function (data, keys, values) {
var returnData = [], i, j, k;
var parsedData = JSON.parse(data);
var keys = keys || null;
var values = values || null;
var datalen = parsedData.length;
for (i = 0; i < datalen; i++) {
returnData[i] = {};
for(j = 0; j< keys.length; j++){
for(k = 0; k < values.length; k++){
returnData[i][keys[j]] = parsedData[i] + values;
}
}
}
return returnData;
};
and then use it like:
var keys = ["foo","bar"];
var values = [".val.name", ".val.date"];
processData(data, keys, values);
But this does not work and in console I see foo="[object Object].val.name" rather than the expected foo="ACME Industries".
If you want to stick to your pattern of constructing the subscript as a string with dots in it you have to roll your own lookup function, like so:
function descend(object, sub) {
var handle = object,
stack = sub.split('.'),
history = [],
peek;
while (handle[stack[0]]) {
if (peek) {
history.push(peek);
}
peek = stack.shift();
handle = handle[peek];
}
if (stack.length > 0) {
history.push(peek);
throw "Traversal error, could not descend to '" + stack.join('.') + "' from '" + history.join('.') + "'.";
}
return handle;
}
var x = {
a: {
b: {
c: 15
},
d: 4
}
};
console.log(descend(x, "a"));
console.log(descend(x, "a.b"));
console.log(descend(x, "a.b.c"));
console.log(descend(x, "a.d"));
function processData(data, keys, values) {
if (keys.length !== values.length) {
throw "Mismatched keys and value lookups";
}
var i,
len = keys.length,
gathered = {},
k,
scratch,
v;
for (i = 0; i < len; i += 1) {
k = descend(data, keys[i]);
scratch = values[i].split('.');
scratch.shift();
v = descend(k, scratch.join('.'));
gathered[keys[i]] = v;
}
return gathered;
}
var data = {
foo: {
val: {
name: "ACME Industries"
}
},
bar: {
val: {
date: (new Date())
}
}
};
var keys = ["foo","bar"];
var values = [".val.name", ".val.date"];
processData(data, keys, values);
Please note: this will not be nearly as performant as coding without this style of lookup.
If you try:
new Object() + '.john.doe'
It will concatenate as a string, so you’ll get "[object Object].john.doe".
You should create a function that can handle dynamic property names instead (and there are plenty of those). You also might want to loose the ".foo.bar" syntax as a string (unless you plan to use eval()) and work solely with arrays instead.
If I understand correctly you need to use
mydata[i]["val"]["name"]
So, I'd use something like this:
var result =getItemByValuesPath(myData[i],values);
alert(result);
function getItemByValuesPath(item, values)
{
var result = item;
var vals = values.split(".");
for(var j=0; j<values.length; j++)
{
if(result==undefined)
{
return null;
}
result = result[values[j]];
}
}