Javascript object value from string path - javascript

I have an object:
var obj= {
hello:{
it:"ciao",
en:"hello"
}
}
Now the problem is that i can't access a value through obj.hello[lang], neither obj.hello.en.
i have a string like 'hello.it' or 'hello.en' and I want to get something like obj[myString] that became obj.hello.en
I tried to split the string with .split('.'), but i have to loop hard coded through the length result
How can i achieve this?

I don't understand... obj.hello.it should work
var obj= {
hello:{
it:"ciao",
en:"hello"
}
}
console.log(obj.hello.it);
If you need to get this value from a string 'hello.it' I would do something like that :
var obj= {
hello:{
it:"ciao",
en:"hello"
}
}
var helloIt = 'hello.it';
var helloEn = 'hello.en';
function translate(s){
var values = s.split('.');
var resource = values[0];
var lang = values[1];
return obj[resource][lang];
}
console.log(translate(helloIt));
console.log(translate(helloEn));
After that you have to manage some cases (if the string has not the right format, if the translation does not exist...). You could manage that everything in a translation module or something... Hope it helps ;)
EDIT :
If you want to have a way to 'explore' objects using a string you can do something like that :
var obj= {
hello:{
it:"ciao",
en:"hello"
}
};
function explore(o, s){
return s.split('.').reduce(function(r, ss){
return r && r[ss] ? r[ss] : null;
}, o);
}
console.log(explore(obj, 'hello.it'))

Hi if your string is something like 'hello.it' you can do this hope it helps
var obj= {
hello:{
it:"ciao",
en:"hello"
}
};
var str = 'hello.it';
alert(obj[t.split('.')[0]][t.split('.')[1]]);

You could split the path and iterate it and reduce the object you have. This proposal works with a default value for missing properties.
var obj= { hello: { it: "ciao", en: "hello" } },
path ='hello.it',
value = path.split('.').reduce(function (o, k) {
return (o || {})[k];
}, obj);
console.log(value);

Try this, it will work for whichever level you want.
var obj= {
hello:{
it:"ciao",
en:"hello"
}
}
var lang='hello.it';
var val=obj;
lang.split('.').forEach(function(c){val=val[c]});
console.log(val);//Should output ciao

Personaly, I don't know why you don't use eval.
var str = "hello.it";
eval("obj." + str);
I thin eval is best if str is hardcoded.
And I think you can refer this.

Here's a sample showing how you can walk down an object:
var obj= {
hello:{
it:"ciao",
en:"hello"
}
}
var path='hello.it'.split('.');
x=obj;
for (var i=0;i<path.length;i++)
{
x=x[path[i]];
console.log(x);
}
Edit To modify the value you can use the same style, but you need to know when your at the string. This really will depend on your requirements and your actual object model but here's an example
for (var i=0;i<path.length;i++)
{
a=x[path[i]];
if(typeof(a)=='string')
{
x[path[i]]='NewString'
console.log(x[path[i]]);
break;
}
x=a
console.log(x);
}

this should work:
var obj= {
hello:{
it: "ciao",
en: "hello"
}
};
var input = "hello.it"; //change this to the value you wish to get.
var str = "obj".concat(".").concat(input);
console.log(eval(str));

Related

Substitute variables in strings like console.log

I want to substitute variables in a string like console.log does.
What I want to achieve is something like this:
let str = 'My %s is %s.';
replaceStr(string, /* args */) {
// I need help with defining this function
};
let newStr = replaceStr(str, 'name', 'Jackie');
console.log(newStr);
// output => My name is Jackie.
/*
This is similar to how console.log does:
// console.log('I\'m %s.', 'Jack');
// => I'm Jack.
*/
I am not able to figure out how to do that. Any help will be much appreciated.
Thank you.
You could prototype it to the String object. Something like this:
String.prototype.sprintf = function() {
var counter = 0;
var args = arguments;
return this.replace(/%s/g, function() {
return args[counter++];
});
};
let str = 'My %s is %s.';
str = str.sprintf('name', 'Alex');
console.log(str); // 'My name is Alex'
You can use spread operator (ES6):
function replaceStr(string, ...placeholders) {
while (placeholders.length > 0) {
string = string.replace('%s', placeholders.shift());
}
return string;
}
EDIT: Based on lexith's answer, we can avoid the explicit loop:
function replaceStr(string, ...placeholders) {
var count = 0;
return string.replace(/%s/g, () => placeholders[count++]);
}
If hope you want to have custom logger function.
console.log can replace %s, with below approach your custom function gets full feature set of console.log and its more efficient.
function myLogger() {
if(logEnabled) {
// you can play with arguments for any customisation's
// arguments[0] is first string
// prepend date in log arguments[0] = (new Date().toString()) + arguments[0] ;
console.log.apply(console, arguments);
}
}
function replaceStr(string, ...placeholders) {
const replaced = string.replace(/%s/g, () => placeholders.shift());
return [replaced, ...placeholders].join(' ');
}
This will append any remaining placeholders to the string to more accurately replicate console.log.

How to use a formatted string to access SUB properties in a JavaScript object?

For the sake of reusable code, I am trying to avoid having to hardcode literally thousands of complex objects by instead using strings like "foo.bar.sub" to access properties.
Now I have quickly worked out a naive algorithm for getting the value of a property, like so:
getValueForPath : function(path) {
var pathArray = path.split('.'),
modelCopy = this;
while (pathArray.length > 0) {
modelCopy = modelCopy[pathArray.shift()];
}
return modelCopy;
},
However this will only return the value of a property, not let me set the value of a property. So it is only one half of the problem. Ideally I need a way to, for a given path, return the property itself, which i'm not sure is possible in JavaScript(But i'm not a JavaScript expert), or I need a second function to set the property for a given path, and I have so far been unable to work this out.
Any thoughts?
If you don't want to use another function, you can use
getValueForPath("foo.bar").sub = something;
Alternatively,
setValueForPath: function(path, val) {
var pathArray = path.split('.'),
modelCopy = this;
while (pathArray.length > 1) {
modelCopy = modelCopy[pathArray.shift()];
}
return modelCopy[pathArray[0]] = val;
}
getValueForPath("foo.bar.sub", something);
Also consider unifying both functions:
accessValueForPath: function(path, val) {
var pathArray = path.split('.'),
modelCopy = this,
b = val!==void 0;
while (pathArray.length > b) {
modelCopy = modelCopy[pathArray.shift()];
}
return b ? modelCopy[pathArray[0]] = val : modelCopy;
}
accessValueForPath("foo.bar.sub"); // read
accessValueForPath("foo.bar.sub", something); // write

new Object with prototype function vs regular function which returns an object

The basic idea is to check if it starts with an underscore and if there is split the string and return whatever comes after the underscore. This function will be run many times, but for different strings, it is unlikely i will need to retrieve the information more than once for each stirng.
A simple function which will return an object with the data I need:
var parseElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
var obj = {
isClass: null,
name: ''
}
if(sliced[0] === '_') {
obj.name = sliced[1];
obj.isClass = true;
} else {
obj.name = i;
obj.isClass = false;
}
return obj
}
Called with parseElementName(i);
Object with prototyped function
var parsedElement = function(i) {
this.className =
this.isClass = null;
if(this.setElementName(i))
return true
}
parsedElement.prototype.setElementName = function(i) {
var sliced = [i.slice(0, 1), i.slice(1, i.length)];
if(sliced[0] === '_') {
this.className = sliced[1];
this.isClass = true
} else {
this.className = i;
this.isClass = false
}
}
Called with var parsed_element = new parsedElement();
then parsed_element.className or parsedElement.isClass
Which approach is recommended?
I like the object prototype approach best, but I have a few notes about your code:
Use semicolons at the end of each line
Class names should be capitalized. So it should be ParsedElement
I wouldn't call it className, because it is confusing when it is not a class, I would rename it name
The two ways have different outcomes - that constructor+prototype approach will yield an instance which has a setElementName method. Will you ever need this to change the fields of an existing object? It's a simple parser function, so I would assume no. In that case, you should go with returning the object literal:
function parseElementName(i) {
var isClass = i.charAt(0) == '_';
return {
isClass: isClass,
name = isClass ? i.slice(1) : i
};
}
If you really need that method later, consider #MaxMeier's and #HMR's points.

pass variable to replace function

How can I pass variable to replace function?
function fname(object, object2){
object.text.replace(/{{[^}}]+}/g,function(m,key){
return object.replaceText[object2[m.substring(2, m.length-2)]];
});
}
objects are similar:
object = {
text : 'some text where I want replace {{some}} string',
replaceText : {
'1' : 'newText'
}
}
object2 = {
some : 1
}
Thanks for help.
EDIT:
I find another way to do this, here is no problem with variable scope:
function fname(object1, object2){
var reg = new RegExp(/{{[^}}]+}}/g);
var result;
while((result = reg.exec(object1.text)) !== null) {
object1.text = object1.text.replace(result[0],object1.replaceText[object2[result[0].substring(2, result[0].length-2)]]);
}
}
Thanks for everyone!
String.prototype.replace returns new string, so change your function like this:
function fname(object1, object2) {
object1.text = object1.text.replace(/{{([^}}]+)}}/g,function(m,key){
return object1.replaceText[object2[key]];
});
}
Also change regexp to /{{([^}}]+)}}/g (note parenthesis). ([^}}]+) is a matching group so you can populate key variable and avoid substring inconvenience.

Is it possible to use regexp as object property?

Let's say I have the following object:
var map = {};
map[/^\/e\/(?:([0-9a-f]{24}))\/rename\/?$/] = "/e/531e8942711f533cf0000019/rename";
The object above is mapping regex to testing value. I need to make something like the following:
for (var i in map) {
if (map.hasOwnProperty(i) && i.test(map[i])) {
console.log('It works');
}
}
But the code above doesn't work. What's wrong?
You can do:
var map = {};
map["^/e/(?:([0-9a-f]{24}))/rename/?$"] = "/e/531e8942711f533cf0000019/rename";
for (var i in map) {
if (map.hasOwnProperty(i) && (new RegExp(i)).test(map[i])) {
console.log('It works');
}
}
// prints: It works
Yes, you can; however, one thing to take note of is that regular expressions are first cast into a string before they're being used as a property name.
So when you want to use a property name as an expression again you must "rehydrate" it first:
for (var i in map) {
if (map.hasOwnProperty(i)) {
var re = eval(i);
if (re.test(map[i])) {
console.log('It works');
}
}
}
However, I would recommend using arrays instead to prevent this "magical" conversion to and fro:
var map = [];
map.push([/^\/e\/(?:([0-9a-f]{24}))\/rename\/?$/, "/e/531e8942711f533cf0000019/rename"]);
map.forEach(function(info) {
if (info[0].match(info[1])) {
console.log('it works');
}
});

Categories

Resources