I can access a variable easily:
function accessVariable(accessFunction, variable) {
var namespaces = accessFunction.split(".");
for (var b = 0; b < namespaces.length; b++) {
if (namespaces[b] != "") {
try {
variable = variable[namespaces[b]];
} catch {
variable = undefined;
};
};
return variable;
};
But updating this gotten variable is something that I don't know how to do.
It's easiest if you use a separate function to update. This can take the new value as another argument.
Then you stop the iteration before the last item in namespace, and use that as the index to assign to.
function updateVariable(accessFunction, variable, value) {
var namespaces = accessFunction.split(".");
for (var b = 0; b < namespaces.length - 1; b++) {
if (namespaces[b] != "") {
try {
variable = variable[namespaces[b]];
} catch {
variable = undefined;
}
}
}
if (variable) {
variable[namespaces.pop()] = value;
}
}
let obj = {a: {b:[{c: 10}]}};
updateVariable('a.b.0.c', obj, 20);
console.log(obj);
Define a single global-scoped variable, and put your variables there.
var Glob = {}; // globally scoped object
function changeVar(){
Glob.variable1 = 'value1';
}
function SeeVar(){
alert(Glob.variable1); // shows 'value1'
}
Related
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 achieves kinda the opposite of what I'm trying to do. Basically, I have this object:
var a = {
b: {
c: 'Foo'
}
}
What I need to do is set the value of c given the string 'b.c'. Unfortunately, I can't do this:
a['b.c'] = 'Bar'
As far as I can tell, the question above doesn't get me anywhere close as it just copies the values of the object properties so they can be read. It doesn't help me set the values of the object properties, however. Here's what I have so far:
var key = 'b.c'.split('.');
for (var i = 0; i < key.length; i++) {
// do something
}
Here's a functional way, is this what you need? It doesn't use the particular a[string] syntax but a function where you can pass the object string and the value to set:
var obj = { foo: { bar: { lol: { lulz: 69 } } } };
function setProp(obj, prop, value) {
var props = prop.split('.');
return [obj].concat(props).reduce(function(a,b,i) {
return i == props.length ? a[b] = value : a[b];
});
}
setProp(obj, 'foo.bar.lol.lulz', 'hello world');
console.log(obj.foo.bar.lol.lulz); //=> "hello world"
You have to intercept the last iteration of the loop, and from there assign instead of redefining your temp variable.
I adapted the answer to the question you linked to assign the value 2 to a.b.c:
var a = {
"b" : {
"c" : 1
}
}
var n = "b.c".split(".");
var x = a;
for(var i = 0; i < n.length; i++){
if(i == n.length-1) {
x[n[i]] = 2;
} else {
x = x[n[i]];
}
}
http://jsfiddle.net/Fuhbb/
This is pretty much what #Ian is doing in his jsfiddle. Intead of an if, he loops one step less, then deals with the assignment after the loop.
For the record, I eventually figured out another way to do this:
function setProperty(obj, props, val) {
if (obj[props[0]] instanceof Object) {
setProperty(obj[props[0]], props.slice(1, props.length), val);
return;
}
obj[props[0]] = val;
}
Call it like this:
a = {
b: {
c: 'Foo'
}
};
var whatever = 'b.c';
var props = whatever.split('.');
setProperty(a, props, 'Bar');
I use the following function to dynamically check the properties of a variable number of objects.
// FUNCTION: Check Objects
var ObjectsN = 4;
function CheckObjects()
{
for (var i=0; i<=ObjectsN; i++)
{
if ((eval("Object"+i+".property")==0)
{
if (i==ObjectsN)
{
alert("Function finished");
}
}
else
{
return; // end function
}
}
}
I need to check if each object has the same property value.
Is there a way to do the same without using eval ?
A real example would be really much appreciated.
The structure you're looking for is an Array. So use an array to store your objects:
/* assuming `var Object1 = new Object(), Object2 = new Object();` and so on */
var objs = [ Object1, Object2, Object3 ];
Then testing for a property should implement hasOwnProperty while iterating:
for (var o = 0; i < objs.length; o++){
if (objs[o].hasOwnProperty('property')){
// Property exists, test its value:
}
}
You could then test against objs[o].property to retrieve its value and see if it matches what you're expecting.
And for those browsers that may not have the function available, here is a cross-browser version of hasOwnProperty (source, but originally from here):
/*
Cross-browser hasOwnProperty solution, based on answers from:
https://stackoverflow.com/questions/135448/how-do-i-check-to-see-if-an-object-has-an-attribute-in-javascript
*/
if ( !Object.prototype.hasOwnProperty ) {
Object.prototype.hasOwnProperty = function(prop) {
var proto = obj.__proto__ || obj.constructor.prototype;
return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
};
}
assuming Object0, Object1, Object2,Object3 & Object4 are global variables.
you could access the variables by using window
var ObjectsN = 4;
function CheckObjects()
{
for (var i=0; i<=ObjectsN; i++)
{
if (window['Object'+i].property == 0)
{
if (i==ObjectsN)
{
alert("Function finished");
}
}
else
{
return; // end function
}
}
}
Whenever you are thinking about numbering variables. Use an array instead.
Don't have Object1, Object2, etc. Have [ x, y, x ]
Then you can do:
for (var i = 0; i < objects.length; i++) {
var obj = objects[i];
if ("property" in obj) {
// Whatever
}
}
Similar to Neverever's answer, but passing a reference to the global object instead of assuming there's a window object available.
var ObjectsN = 4;
var CheckObjects = (function(global) {
for (var i=0; i<=ObjectsN; i++) {
if ( global['Object' + i].property == 0) {
if (i == ObjectsN) {
alert("Function finished");
}
} else {
return; // end function
}
}
}
Are you sure that == 0 is the test you want? That will return true even if the property doesn't exist. The function isn't particularly robust, if an object from 0 to n doesn't exist, an error will be thrown.
The function will continue only as long as Objectn.property == 0 returns true, so it will exit as soon as it gets a truethy result (e.g. Object0.property = 'foo').
Say i have this function that dynamically creates my namespace for me when I just pass it a string, (I'm pretty sure basically what YUI JS library does):
MyObj.namespace('fn.method.name');
would result in
MyObj.fn.method.name = {}
being created - all three levels being equivalent to an empty object.
Now, what I want to do, though, is make the last level, in this case name, set to a function, but without having to redeclare the newly created object.
So instead of doing this:
function fnName() { /* some code here */ }
MyObj.namespace('fn.method.name');
MyObj.fn.method.name = new fnName();
i want to call something like:
MyObj.add('fn.method.name', fnName);
And internally, the add method would programmatically instantiate the passed in function:
MyObj.fn.method.name = new fnName()
In the way I have it implemented, I can create the namespace object and set it to an empty object, however, when I try to instantiate a passed in function and associate that namespace with the passed in function, it never gets added to the namespace. Instead, an empty object is always returned. Any ideas?
edit: Here is the namespace method. this is attached to the base object as a JSON object, so please ignore the formatting:
namespace: function (ns) {
var _ns = ns.split('.'),
i = 0, nsLen = _ns.length,
root = this;
if (_ns[0] === gNS) {
_ns.shift();
nsLen = _ns.length;
}
for (i = 0; i < nsLen; i++) {
// create a property if it doesn't exist
var newNs = _ns[i];
if (typeof root[newNs] === "undefined") {
root[newNs] = {};
}
root = root[newNs];
}
return root;
}
edit2 - removed the passed in fn argument
Were you looking for something like this:
var root = {};
function create(ns, fn) {
var nsArray = ns.split(/\./);
var currentNode = root;
while(nsArray.length > 1) {
var newNS = nsArray.shift();
if(typeof currentNode[newNS] === "undefined") {
currentNode[newNS] = {};
}
currentNode = currentNode[newNS];
}
if(fn) {
currentNode[nsArray.shift()] = fn;
}
else {
currentNode[nsArray.shift()] = {};
}
}
Then:
create("a.b.c");
console.log(root.a);
console.log(root.a.b);
console.log(root.a.b.c);
Gives:
Object { b={...}}
Object { c={...}}
Object {}
And:
create("d.e.f", function() { console.log("o hai"); });
console.log(root.d);
console.log(root.d.e);
console.log(root.d.e.f);
Gives:
Object { e={...}}
Object {}
function()
Calling the function you defined:
root.d.e.f();
Gives:
o hai
Well you haven't given the namespace function but your add function could look something like this:
MyObj.add = function (namespace, value) {
var names = namespace.split('.'), current = this, name;
while (names.length > 1) {
name = names.shift();
current[name] = {};
current = current[name];
}
current[names[0]] = value;
};
This code assigns the value given to the last part of the namespace. You could modify it to current[names[0] = new value(); if you want the object constructed by the passed in function (and you are assuming the constructor function takes no arguments).
function ns() {
var root = window;
for (var i = 0; i < arguments.length; i++) {
var arr = arguments[i].split(/\./);
for (var j = 0; j < arr.length; j++) {
var item = arr[j];
if (typeof item !== 'string') {
root = item;
}
else {
if (!root[item]) {
root[item] = {};
}
root = root[item];
}
}
root = window;
}
}
then you can create using
ns('fn.method.name');
or
ns('fn.method.name','fn.method.secondName');
and call using
fn.method.name
this function creates your namespace on 'window' so alternatively you can use
window.fn.method.name