Using variables with nested Javascript object - javascript

Suppose I have this:
var a = { A : { AA : 1 }, B : 2 };
Is there a way for me to create a variable that could allow me to reference either AA or B? What would the syntax look like?
// I know I can do this:
a['B']; // 2
a['A']['AA']; // 1
// something like this?
var myRef = ???;
a[myRef]; 1 or 2 depending on myRef
If not, what's a better way to get what I'm going for here?

Not directly.
Solution 1 - use object flattening
Flatten object, to have new object var a = { 'A.AA' : 1; B : 2 };.
See compressing object hierarchies in JavaScript
or Flattening a complex json object for mvc binding to get the javascript function for it.
Soution 2 - write key-path accessor
I can see it was already addressed by Eugen.
Reposted code-reviewed version:
function Leaf(obj,path) {
path=path.split('.');
var res=obj;
for (var i=0;i<path.length;i++) res=res[path[i]];
return res;
}
Solution 3 - use eval
var x = eval("a." + myRef); // x will be 1 for myRef == "A.AA", 2 for "B"
Be careful with this solution as you may introduce some security issues. It is more of the curiosity.

Since i also encounter this problem, i wrote also a one line util for this (ES6):
const leaf = (obj, path) => (path.split('.').reduce((value,el) => value[el], obj))
Example:
const objSample = { owner: { name: 'Neo' } };
const pathSample = 'owner.name';
leaf(objSample, pathSample) //'Neo'

function Leaf(obj,path) {
path=path.split('.');
var res=obj;
for (var i=0;i<path.length;i++) obj=obj[path[i]];
return res;
}
Leaf(a,'B')=2
Leaf(a,'A.AA')=1
Decorate with error handling etc. according to your needs.

With lodash _.get function, you can access nested properties with dot syntax.
Node server-side example:
const _ = require('lodash');
let item = { a: {b:'AA'}};
_.get(item, 'a.b');

Actually no, because js object are seen as property bags and doing a[X] is for accessing first level properties only...
But you could wrap the logic a['A']['AA']; // 1 in a function that does the same, like this
//WARN... no undefined check here => todo !
function _(o, path) {
var tmp = o
for (var i=0 ; i < path.length ; i++) {
tmp = tmp[path[i]]
}
return tmp
}
var r = _(a, ['A', 'AA'])
This is pretty much the same as other answers, but the difference is when dummy boy create object property name containing dots... Like var a = {"a.a" : 3 } is valid.
Now, such problem would occurs maybe more often now with the help of IndexedDB to store anything locally...

Related

Javascript: How to create an object from a dot separated string?

I ran into this potential scenario that I posed to a few of my employees as a test question. I can think of a couple ways to solve this problem, but neither of them are very pretty. I was wondering what solutions might be best for this as well as any optimization tips. Here's the question:
Given some arbitrary string "mystr" in dot notation (e.g. mystr = "node1.node2.node3.node4") at any length, write a function called "expand" that will create each of these items as a new node layer in a js object. For the example above, it should output the following, given that my object name is "blah":
blah: { node1: { node2: { node3: { node4: {}}}}}
From the function call:
mystr = "node1.node2.node3.node4";
blah = {};
expand(blah,mystr);
Alternately, if easier, the function could be created to set a variable as a returned value:
mystr = "node1.node2.node3.node4";
blah = expand(mystr);
Extra credit: have an optional function parameter that will set the value of the last node. So, if I called my function "expand" and called it like so: expand(blah, mystr, "value"), the output should give the same as before but with node4 = "value" instead of {}.
In ES6 you can do it like this:
const expand = (str, defaultVal = {}) => {
return str.split('.').reduceRight((acc, currentVal) => {
return {
[currentVal]: acc
}
}, defaultVal)
}
const blah = expand('a.b.c.d', 'last value')
console.log(blah)
Here's a method that popped up in my mind. It splits the string on the dot notation, and then loops through the nodes to create objects inside of objects, using a 'shifting reference' (not sure if that's the right term though).
The object output within the function contains the full object being built throughout the function, but ref keeps a reference that shifts to deeper and deeper within output, as new sub-objects are created in the for-loop.
Finally, the last value is applied to the last given name.
function expand(str, value)
{
var items = mystr.split(".") // split on dot notation
var output = {} // prepare an empty object, to fill later
var ref = output // keep a reference of the new object
// loop through all nodes, except the last one
for(var i = 0; i < items.length - 1; i ++)
{
ref[items[i]] = {} // create a new element inside the reference
ref = ref[items[i]] // shift the reference to the newly created object
}
ref[items[items.length - 1]] = value // apply the final value
return output // return the full object
}
The object is then returned, so this notation can be used:
mystr = "node1.node2.node3.node4";
blah = expand(mystr, "lastvalue");
var obj = {a:{b:{c:"a"}}};
const path = "a.b.c".split(".");
while(path.length > 1){
obj = obj[path.shift()];
}
obj[path.shift()] = "a";

Access to object and set it null throw element of array

Very simple example:
var a = { id: 5 };
var b = { id: 6 };
var c = { id: 7 };
var arr = [a, b, c];
Now i have a function:
function remove(startIndex) {
// set objects to null from startIndex in array
}
If i try this:
arr[0] = null;
then i have:
arr[0] == null // true
a == null // false (i need true)
So, my question, how could i access to object throw any collection (array or object) and change it?
I don't want to write something like this:
function remove(startIndex) {
if(startIndex == 0) {
a = null;
b = null;
c = null;
}
if(startIndex == 1) {
b = null;
c = null;
}
if(startIndex == 2) {
c = null;
}
}
much easier to write like this (but it doesn't work):
function remove(startIndex) {
for(var i = startIndex; i<arr.length; i++) arr[i] = null;
}
I don't know exactly what you're aiming with this code you're writing, but here's how Javascript works:
Every time you instantiate a variable with a value, say an object like { id: 10 }. That object is stored in memory and a reference is returned back to your variable, say you name it a.
Now, if you say var b = a;, the same reference is now passed on to variable b. Now Javascript runtime knows you have two variables referencing the object { id: 10 }.
You now no longer want to keep the variable b, so you write b = null;. You think the object is deleted, but the Javascript runtime knows the object { id: 10 } has one reference -- i.e. the variable a -- referencing it. So it won't remove { id: 10 } from memory.
However, if you also write a = null;, then there are Zero references, and the Javascript runtime's Garbage Collector will eventually get to removing the object from memory.
All this was to get you to understand that without further housekeeping, you will not be able to achieve what you're hoping to do.
If you really want a, b, c to be null, you will have to write some code that explicitly sets their value to null too. Like a = arr[0]; b = arr[1]; c = arr[2]; whenever the array is changed. You can eval the statements and do some string templating to not write the variables by hand etc., and make a loop out of that, but that's not worth it if you only have three variables.
Hope this helps.
JavaScript doesn't have pointers, so to achieve what you want in the original way will not work.
Other than arr[0] = null, you can try setting a to null directly as well.

Javascript: Server sided dynamic variable names

How would I create dynamic variable names in NodeJS? Some examples say to store in the window variable, but I was assuming that is client-side Javascript. Correct me if I'm wrong.
Generally you would do something like:
var myVariables = {};
var variableName = 'foo';
myVariables[variableName] = 42;
myVariables.foo // = 42
In node.js there is the global context, which is the equivalent of the window context in client-side js. Declaring a variable outside of any closure/function/module as you would in plain Javascript will make it reside in the global context, that is, as a property of global.
I understand from your question that you want something akin to the following:
var something = 42;
var varname = "something";
console.log(window[varname]);
This in node.js would become:
var something = 42;
var varname = "something";
console.log(global[varname]);
Just don't know what a bad answer gets so many votes. It's quite easy answer but you make it complex.
var type = 'article';
this[type+'_count'] = 1000; // in a function we use "this";
alert(article_count);
One possible solution may be:
Using REST parameter, one can create an array and add each dynamic variable (REST parameter item) as an object to that array.
// function for handling a dynamic list of variables using REST parameters
const dynamicVars = (...theArgs) => {
let tempDynamicVars = [];
// as long as there are arguments, a new object is added to the array dynamicVars, creating a dynamic object list of variables
for (let args = 0; args < theArgs.length; args++){
const vName = `v${args}`;
tempDynamicVars = [...tempDynamicVars, {[vName]: theArgs[args]}]; //using spread operator
// dynamicVars.push({[vName]: theArgs[args]}); // or using push - same output
}
return tempDynamicVars;
}
// short version from above
// const dynamicVars = (...theArgs) => theArgs.map((e, i) => ({[`v${i}`]: e}));
// checking
const first = dynamicVars("F", 321);
console.log("Dynamic variable array:", first);
console.log(` - ${first.length} dynamic variables`);
console.log(" - second variable in the list is:", first[1], "\n");
console.log(dynamicVars("x, y, z"));
console.log(dynamicVars(1, 2, 3));
console.log(dynamicVars("a", "b", "c", "d"));

javascript get json inner value

Let's I have next object
var o = { "foo" : {"bar" : "omg"} };
I can get value of key foo using
o["foo"] // return {"bar" : "omg"}
and I can get value of key bar inside foo using
o["foo"]["bar"] // return "omg"
Can I get value of key bar inside foo using brackets [] single time.
Somethong like
o["foo.bar"] // not working(
or
o["foo/bar"] // not working(
It is fairly common to create a getter function to do something like this. From the comment:
I have object o and string 'foo.bar', and i want get "omg".
var getProp = function (theObject, propString) {
var current = theObject;
var split = propString.split('.');
for (var i = 0; i < split.length; i++) {
if (current.hasOwnProperty(split[i])) {
current = current[split[i]];
}
}
return current;
};
http://jsfiddle.net/MXu2M/
Note: this is a thrown together example, you'd want to bullet proof and buff it up before dropping it on your site.
No, you must use o["foo"]["bar"] because it's an object inside another object. If you want to access it with "foo.bar", it means you must create the first object like this:
var o = {"foo.bar": "omg"}
o["foo.bar"] or o["foo/bar"] are not valid for your example. You could use this notation that is cleaner:
var bar = o.foo.bar // bar will contain 'omg'
there is a way, but I'm not sure this is what you asked for:
eval("o.foo.bar");
it is dangerous though, and doesn't use [] , but if what you want is to use a string for accessing any object it works
Unfortunately, you can only use o["foo"]["bar"] or o.foo.bar

Object (string or array) NAME. how to get it?

I need a prototype done in this way:
Array.prototype.getname=function(){ [...]return arrayname; }
So I can:
z=new Array;
alert(z.name);
and I should have "z" in the alert.
I'm working on Chrome and caller/callee seem to return empty.
The best you can do is to always explicitly set the array's name when you create it:
var z = [];
z.name = 'z';
You could do this by having a function makeArray that sets the name passed as an argument:
function makeArray(name) {
var arr = [];
arr.name = name;
return arr;
}
The essential problem is that the several variables can point to the same array:
var z = [],
x = z;
Then what should the name be: z or x?
The problem is that a variable (like an array) can have several names. For example:
var a = new Array();
var b = a;
a[0] = "hello";
alert(b[0]);//"hello"
What is the name of the array, a or b?
Can't be done. There is no way to access the name of the variable which is storing a reference to the object. Perhaps you should explain why you need behavior like this, so someone can suggest you an alternative way to approach the problem.
The only way to do this would be to brute-force check all properties of the global object (assuming the variable is global) until you find a property that === the array. Of course, it could be referenced by multiple variables so you would have to pick one of the names you get. This implementation gets the first variable to reference the array and will work in browsers and web worker threads:
Array.prototype.getName = function () {
var prop;
for (prop in self) {
if (Object.prototype.hasOwnProperty.call(self, prop) && self[prop] === this) {
return prop;
}
}
return ""; // no name found
};
Of course, I don't recommend this at all. Do not do this.
Further testing the object.getName().. i found this 'problem':
test1='test'
test
test2='test'
test
test1.getName()
test1
test2.getName()
test1
this happens because they have the same content and getName checks for content.
a solution could be to return an Array in that case.
But I'm still searching for a definitive solution to this brainteasing problem.
So For now the best answer is Elijah's
More generally:
Object.prototype.getName = function () {
var prop;
for (prop in self) {
if (Object.prototype.hasOwnProperty.call(self, prop) && self[prop] === this && self[prop].constructor == this.constructor) {
return prop;
}
}
return ""; // no name found
};
I wonder if there are better solutions.

Categories

Resources