I have an variable obj
defined as follows:
{user: {username: "AzureDiamond", password: "hunter2"}}
I have a string str
that might be defined as any of the following strings:
Is there a relatively easy way for me in JS (specifically node.js) to essentially/dynamically perform the following?
str == "user"
, return {username: "AzureDiamon", password: "hunter2"}
str == "user[username]"
, return "AzureDiamond"str == "user[password]"
, return "hunter2"str == "fake"
, return null
Edit: This question is more to find out if there's anything easier or build into JS/node.js that I can use other than regex matches.
If you are sure the syntax of str
is correct, the function could look like this:
function get_by_path(source, path){
var path_elements = path.split(']').join('').split('[');
for (var i=0; i < path_elements.length; i++){
source = source[path_elements[i]];
if (typeof source === 'undefined'){
return null;
}
};
return source;
};
This is how it behaves:
var data = {'user': {
'username': "AzureDiamond", 'password': "hunter2",
'details': {'address': {'street': "Happiness Street"}}
}};
get_by_path(data, 'user'); // gives user object
get_by_path(data, 'user[username]'); // gives "hunter2"
get_by_path(data, 'user[password]'); // gives "AzureDiamond"
get_by_path(data, 'fake'); // gives null
get_by_path(data, 'user[details][address][street]'); // gives "Happiness Street"
The script does not use regular expression, nor dangerous eval()
calls, only assumes your "path" will start with one word without brackets and every next part (if any) will be word enclosed in square brackets. If some path will not be found within the traversed object, null
will be returned. If it will be found, it will be returned (regardless of whether it will be some complex object, string, null
, boolean or anything else).
Script begins with parsing your path. It does that by removing closing square brackets ("]
") and splitting resulting string by opening square brackets ("[
"). The process looks like this:
"user" -> "user" -> ["user"]
"user[username]" -> "user[username" -> ["user", "username"]
"user[password]" -> "user[password" -> ["user", "password"]
"user[address][street]" -> "user[address[street" -> ["user", "address", "street"]
"fake" -> "fake" -> ["fake"]
So, as you see, at the end you have an array of path elements. These are evaluated one by one. If at any level appropriate step cannot be made deeper into the structure of the data argument, then null
is returned. Otherwise the last accessed element is returned.
Proof that it works is here: http://jsfiddle.net/EJCgE/2/
EDIT: There was some issue with more complex paths (when there were more than two levels), resulting from how string's replace()
method works. I have updated my code to not include jQuery and to fix that issue.
EDIT2: I have updated the script to remove redundant lines and one redundant variable.
This is taken directly from the EXTJS3 source code but I've used it many times to do similar things.
createAccessor : function(){
var re = /[\[\.]/;
return function(expr) {
if(Ext.isEmpty(expr)){
return Ext.emptyFn;
}
if(Ext.isFunction(expr)){
return expr;
}
var i = String(expr).search(re);
if(i >= 0){
return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
}
return function(obj){
return obj[expr];
};
};
}(),
You would use this like so:
var accessor = createAccessor(str);
var data = accessor(object);
Try the eval
function. It takes a string, and executes that string as if it were javascript code. For example:
var data = {user: {username: "AzureDiamond", password: "hunter2"}};
var str = "user[\"password\"]";
console.log( eval("data." + str) );
// Prints "hunter2"
You have to be VERY careful with this though, because depending on where that str
comes from, people could essentially run code on your server remotely. Use a regex to validate the string before execution, to make sure that people can't send you sneaky information and hack your server to bits.
EDIT: Note that you also have to quote password
so that it's treated as a string when executed, not a variable.