Interpolating values into a predefined backtick template literal string? - javascript

I'd like to do something like this:
let template = `The first value is ${v1} and the second value is ${v2}`;
template.interpolate(v1,v2);
Is this possible in Javascript / Typescript? The documentation I've found contains the variables being interpolated into the template within the same context that the template string is created in.
As indicated in the comments there are a lot of ways to do this in Javascript, but I'm specifically wondering whether we can create predefined templates with backticks? For example the duplicate link uses quotes, which do not support multiline template markup.

According to the MDN template literals reference you can create your own templates using backticks, please refer to the Tagged Templates section.
Adapting to your example:
function template(strings, ...keys) {
return function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
};
}
var interpolate = template`The first value is ${'v1'} and
the second value is ${'v2'}`;
interpolate({ v1: 1, v2: 2 });
Whose output should be:
The first value is 1 and
the second value is 2
Hope it helps

Thanks #RodrigoFerreira. I read up on the template literals section and your answer and realized that it's as simple as defining a function and passing that around instead of using a direct template literal:
const f = (v1:any)=> {
return `v1 is ${v1}`;
}
console.log(f("Howdy Rodrigo!")); //logs v1 is Howdy Rodrigo!

Related

How to split ``(Backtick string) on each instance of ${variable}

So my question is how i can split the a string with backtick on each instance of variable.
I tried with \${.*?} but this will not work because ${variable} will be replaced by variable values first and than the split function will be executed.
Any idea how to do it ?
let a = 2
let b = 4
let x = `Superman${a}hello${b}one more`.split(/\${.*?}/g)
console.log(x)
On side not: I don't want a solution with wrapping it to " or '.
console.log('`Superman${a}hello${b}one more`'.split(/\${.*?}/g))
After the line executes, there is no way to get the original template string. However, you can use a tag function/tagged template literal to get the parts of the string, including the substitution values:
function Test() {
console.log(arguments)
return arguments.length - 1
}
let a = 2
let b = 4
let c = Test `Superman${a}hello${b}one more`
console.log(`This template string has ${c} substituted values`)
To clarify my comment to the original question here is an example of what the default Template Literal Function does:
function defaultTemplateLiteralFn(strs, ...args) {
return strs.map((str, idx) => str+(args[idx]||'')).join('');
}
const test = "special test";
const a = 10;
const b = 432;
console.log(`This is a ${test}. "${a}+${b}=${a+b}"`)
console.log(defaultTemplateLiteralFn`This is a ${test}. "${a}+${b}=${a+b}"`)
When you use a tagged template (IE: You don't provide a function to handle the template literal) The the language provides a default function that does something similar to what I do in my function defaultTemplateLiteralFn above. It returns the combined parts of the string with the values.
The example function takes each part of the string and puts the appropriate value after the string. If there is no value then it uses a blank string.
One way i have done is using template literal. i have seen this is being used in a in a library called styled-components which allows us to write css with js.
Would love to see other methods if there are any ?
function splitOnVariable(str, age){
// first argument to function will always be array of strings provided in input string.
return str
}
let a = 1;
let b = 2;
console.log(splitOnVariable`hello${a} break me on variable${b} !!!`)

Function without parentheses returns a weird output [duplicate]

This question already has answers here:
Backticks (`…`) calling a function in JavaScript
(3 answers)
Closed 4 years ago.
Just asking to understand how it works:
function say(something) {
return something;
}
let name = `Reza`;
console.log(say `My name is`, name, '!');
It returns a very weird output. I think that My name is is a string within an array and everything else is just a string (correct me if I am wrong).
My Question is, what is the point of that and when would it make sense to use a function like that?
Also I would be glad if someone could tell me why My name is ${name} is not working (name returns as an empty string).
PS: I know that could use the function with parentheses and it would work but that's not my question.
why My name is ${name} is not working (name returns as an empty
string).
This is because you have to pull out the value of My name is from the array of string values and return it
Try this:
function say(something) {
var str0 = something[0]
return str0;
}
let name = `Reza`;
console.log(say`My name is${name}`, name, '!'); // My name is Reza !
We will receive thing like this:
I would like to point out that the ${name} does not mean anything at all, because inside the tag function, we didn't do anything with it.
This is the result after running your codes on the Chrome devtools:
Let's see how we can explain what is actually going on. Well then, what you're using is called Tagged templates:
A more advanced form of template literals are tagged templates. Tags allow you to parse template literals with a function. The first argument of a tag function contains an array of string values.
That's why we see an array in the result:
For instance, if we run this codes on Chrome devtools:
function say(something) {
return something;
}
console.log(say`anything`);
On the console tab, we will receive this result:
For more information, you could read it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
As we can see, the weird raw property, according to MDN - Raw strings, it is:
The special raw property, available on the first function argument of tagged templates, allows you to access the raw strings as they were entered, without processing escape sequences.
For example:
function tag(strings) {
console.log(strings.raw[0]);
}
tag`string text line 1 \n string text line 2`;
// logs "string text line 1 \n string text line 2" ,
// including the two characters '\' and 'n'
And you are using the console.log() method, according to MDN-Console.log() we have its signature:
Syntax: Console.log()
console.log(obj1 [, obj2, ..., objN]);
console.log(msg [, subst1, ..., substN]);
Parameters
obj1 ... objN
A list of JavaScript objects to output. The string representations of
each of these objects are appended together in the order listed and
output.
msg
A JavaScript string containing zero or more substitution strings.
subst1 ... substN
JavaScript objects with which to replace substitution strings within
msg. This gives you additional control over the format of the output.
In your case, you're passing multiple arguments into the Console.log() method, hence it will work as the document said here: Outputting multiple objects
Doing My name is ${name} should work. Its because your passing My name is to the say function as a tagged template literal, which by default passes the arguments as an array
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
say tag is missing the arguments to the placeholders in the template string:
function shout(parts, name/*<--HERE*/) {
return `${parts[0]}${name}${parts[1]}!!!`;
}
let name = `Reza`;
console.log(shout `My name is ${name}`);
say receives data from the template string as a split string around the placeholders:
[
"My name is ",
"!"
] Reza
Look at this example from the MDN Documentation:
function myTag(strings, personExp, ageExp) {
var str0 = strings[0]; // "That "
var str1 = strings[1]; // " is a "
var ageStr;
if (ageExp > 99){
ageStr = 'centenarian';
} else {
ageStr = 'youngster';
}
// We can even return a string built using a template literal
return `${str0}${personExp}${str1}${ageStr}`;
}
var person = 'Mike';
var age = 28;
var output = myTag`That ${ person } is a ${ age }`;
Your tagged template has a missing argument, it should include a strings array argument as first argument.
function say(strings, something) {
return strings[0] + "Mr. " + something +"!";
}
let name = `Reza`;
console.log(say `My name is ${name}`);
Make it simple
console.log `Hi there`;
why My name is ${name} is not working (name returns as an empty string).
console.log(say `My name is`, name, '!'); /* I don't see `${name}` here */
Also, name was never passed as an argument to say function!
function say(something) {
return something;
}
let name = `Reza`;
/* say without () */
console.log(say `My name is ${name} !`);
/* say with () */
console.log(say(`My name is ${name} !`));
let who = 'everyone';
console.log `Hello ${who}`; /* note, no -> () */
console.log(`Hello ${who}`);
I've spent hours researching documents in order to answer your question. And I'll try my best to explain it simply.
My first answer is, in fact, a text explanation. However, I remember W3Schools once said that:
Examples are better than 1000 words. Examples are often easier to
understand than text explanations.
Well then, this answer supplements all explanations with clarifying "Try it Yourself" examples.
There are two concepts in Javascript that involved: the Tagged templates and the Console.log() method.
I'll re-write your codes, breaking them into two parts, like this: (the executed result remains the same).
// part 1
function say(something) {
return something;
}
let name = `Reza`;
let output = say`My name is`;
// part 2
console.log(output, name, '!');
Let's see how these two parts related to the concepts of Tagged templates and the Console.log() method, respectively.
First, examine this example:
Example 1:
function say(something) {
return something;
}
let output = say`My name is`;
console.log(output)
After running this example, we'll see an array that contains one element which is a string: "My name is"
This is the behavior of the Tagged templates
Before we actually dive into how Tagged templates works, let's take a look at some examples below:
Example 2:
function say(something) {
return something[0];
}
let output = say`My name is`;
console.log(output)
After running the example 2, we'll receive a string: "My name is", instead of an array as the example 1 did.
Well then, what if I want to pass a name to it? see this:
Example 3
function say(something, name) {
return `${something[0]}${name}`;
}
let name = 'Reza';
let output = say`My name is ${name}`;
console.log(output)
Yay! It works as expected. Well then, what if I want to pass two arguments instead of one? see this:
Example 4
function say(something, name, age) {
let str0 = something[0];
let str1 = something[1];
let str2 = something[2];
return `${str0}${name}${str1}${age}${str2}`;
}
let name = 'Reza';
let age = 1000;
let output = say`My name is ${name}, I'm ${age} years old!`;
console.log(output)
Quite clear, right? This is how we use Tagged templates.
The examination of the Tagged templates is done now. Let's see how Console.log() method works.
Example 5
console.log(`My name is `)
We are all familiar with this usage of Console.log() method, passing one argument. Well then, what if I want to pass multiple arguments to it. See this:
Example 6
let name = `Reza`;
console.log(`My name is `, name, '!')
It combined all together into one single output.
Well then, for now, let's go back to your sample:
function say(something) {
return something; // the `something` is still an array
}
let name = `Reza`;
console.log(say `My name is`, name, '!');
// the console.log() method will try to combine all these three arguments
// into one single output
// this is why we got the output as an array with a string: "name" and a string: "!"
We could fix it like this:
Example 7
function say(something) {
return something[0];
}
let name = `Reza`;
console.log(say`My name is`, name, '!');
Or like this:
Example 8
function say(something, name) {
return `${something[0]}${name}`;
}
let name = `Reza`;
console.log(say`My name is ${name} `, '!');
However, let's see this example:
Example 9
function say(something, name) {
return `${something[0]}${name}`;
}
let name = `Reza`;
console.log(say`My name is ${name} `, name, '!');
We'll receive My name is Reza Reza !. I believe that, for now, we all understand why we got it.

Reference JS object through concatenation

I am trying to call an object.
The way I am currently doing it:
var key = object_0
The way I'd like to do it
var key = "object_" + questionId;
But when calling the concatenated object_0 I do not get the object info
when I do a:
console.log(key)
Any insight would be awesome.
Short answer: global scope + brackets.
window['object_'+questionId] = 'foo';
console.log(window['object_'+questionId]); // 'foo'
Long answer: Use dynamic variable names in JavaScript
If you use ES5 you can do so with creating new empty object. Here are the steps:
1.create empty object
var o = {};
2. use brackets to produce the new key on an object - ("object_" + questionId) - this will force the parser first to evaluate expression in the brackets
3.assign value to a newly added key
o[("object_" + questionId)] = XXX;
Then console.log(o) will output {object_0: XXX}
You can use the window object (access to global variables only):
var object_0 = { p: 42 },
questionId = 0,
key = "object_" + questionId;
document.write(window[key].p);
But i suggest to change the data structure to a more concise style:
var questions = { 200: { p: 42 }},
questionId = 200;
document.write(questions[questionId].p);

Reference another field inside a javascript object

I have an object in javascript:
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: function() {
return path; // !!! how to reference the 'path'?
}
}
}
I have a lot of objects like this, and each of them has a path field and a link function. I want to use the field path in link, but I can't just use path.
What should I do?
You can use this to reference the object. Standard object.method() "dot" syntax will set this to object within method:
var someObj = {
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: function() {
return this.path; // !!! how to reference the 'path'?
}
}
}
};
var returnedPath = someObj.admins.articles.link();
Demo: http://jsfiddle.net/2Pt7n/
(There are other ways to call a function such that this will not be set to the appropriate object, but I hope they don't apply here - you don't really say how you're using the objects or calling the function, but if not in the way I showed then please update your question and I'll update my answer accordingly.)
I'll just point out that you don't want to use ES6 fat arrow here, because there will be no this pointer in that case:
var someObj = {
admins: {
articles: {
path: '/admins/articles',
template: '/views/admins/articles.html',
link: () => {
return this.path; // 'this' is undefined
}
}
}
};
someObj.admins.articles.link() === undefined
What you are showing is not JSON. It is a Javascript object, which is different than JSON. JSON is a strictly defined data serialization format that is a subset of Javascript object literals.
Javascript provides no syntax for referencing peer properties in an object literal, as you want to do. Naming them is one idea, but it won't help, because the name won't exist while the literal is being defined, so the name is not available for you to use in the literal itself.
Also, note that the syntax you define makes the object lop-sided: you can access path as obj.admins.articles.path, but link is a function you would have to invoke: obj.admins.articles.link().
I won't talk about how this is not JSON (others covered it well).
You can do this to get path:
return admins.articles.path;
Here's a fiddle to show it working: http://jsfiddle.net/UwbLt/
I'm reading the answers and even understanding the point of some users (that JSON should be used just for data) and agreeing that this is correct, I just created a proof of concept example. Take a look.
// just a regular object
var obj = {
a: "aaa",
b: "bbb",
c: function() {
return this.a;
}
};
console.log( obj.c() ); // prints "aaa"
// isn't it json just because it has a function? ExtJS will treat it like JSON, but jQuery not
var json = "{" +
"\"a\": \"aaa\", " +
"\"b\": \"bbb\", " +
"\"c\": function() {" +
" return this.a;" +
"}" +
"}";
// ok, the "json" above
console.log( json );
//var jsonObj = $.parseJSON( json ); // does not work
//var jsonObj = eval( json ); // does not work too
var jsonObj = Ext.decode( json ); // it works! shortcut for Ext.JSON.decode
console.log( jsonObj.c() ); // prints "aaa"
It is almost the same that nnnnnn posted, but I think I would post it too, just to complement the answers. jsFiddle: http://jsfiddle.net/davidbuzatto/rhKAM/
So I think, even contradicting the definition of JSON, that JSON maybe can have (or should have?) the same characteristics of a object created using the regular object initializer sintax, since its name is JavaScript Object Notation, not "Lightweight" Object Notation. I know, I know, a deserializer won't be able to deserialize a function depending on the target language, but why ExtJS supports this "behavior"? A good discussion can be found here: Is it valid to define functions in JSON results?
Just to clarify. I don't use (and I won't use too) functions inside my JSONs.

Determine original name of variable after its passed to a function

I've got a feeling this might not be possible, but I would like to determine the original variable name of a variable which has been passed to a function in javascript. I don't know how to explain it any better than that, so see if this example makes sense.
function getVariableName(unknownVariable){
return unknownVariable.originalName;
}
getVariableName(foo); //returns string "foo";
getVariableName(bar); //returns string "bar";
This is for a jquery plugin i'm working on, and i would like to be able to display the name of the variable which is passed to a "debug" function.
You're right, this is very much impossible in any sane way, since only the value gets passed into the function.
This is now somehow possible thanks to ES6:
function getVariableName(unknownVariableInAHash){
return Object.keys(unknownVariableInAHash)[0]
}
const foo = 42
const bar = 'baz'
console.log(getVariableName({foo})) //returns string "foo"
console.log(getVariableName({bar})) //returns string "bar"
The only (small) catch is that you have to wrap your unknown variable between {}, which is no big deal.
As you want debugging (show name of var and value of var),
I've been looking for it too, and just want to share my finding.
It is not by retrieving the name of the var from the var but the other way around : retrieve the value of the var from the name (as string) of the var.
It is possible to do it without eval, and with very simple code, at the condition you pass your var into the function with quotes around it, and you declare the variable globally :
foo = 'bar';
debug('foo');
function debug(Variable) {
var Value = this[Variable]; // in that occurrence, it is equivalent to
// this['foo'] which is the syntax to call the global variable foo
console.log(Variable + " is " + Value); // print "foo is bar"
}
Well, all the global variables are properties of global object (this or window), aren't they?
So when I wanted to find out the name of my variables, I made following function:
var getName = function(variable) {
for (var prop in window) {
if (variable === window[prop]) {
return prop;
}
}
}
var helloWorld = "Hello World!";
console.log(getName(helloWorld)); // "helloWorld"
Sometimes doesn't work, for example, if 2 strings are created without new operator and have the same value.
Global w/string method
Here is a technique that you can use to keep the name and the value of the variable.
// Set up a global variable called g
var g = {};
// All other variables should be defined as properties of this global object
g.foo = 'hello';
g.bar = 'world';
// Setup function
function doStuff(str) {
if (str in g) {
var name = str;
var value = g[str];
// Do stuff with the variable name and the variable value here
// For this example, simply print to console
console.log(name, value);
} else {
console.error('Oh snap! That variable does not exist!');
}
}
// Call the function
doStuff('foo'); // log: foo hello
doStuff('bar'); // log: bar world
doStuff('fakeVariable'); // error: Oh snap! That variable does not exist!
This is effectively creating a dictionary that maps variable names to their value. This probably won't work for your existing code without refactoring every variable. But using this style, you can achieve a solution for this type of problem.
ES6 object method
In ES6/ES2015, you are able to initialize an object with name and value which can almost achieve what you are trying to do.
function getVariableName(unknownVariable) {
return Object.keys(unknownVariable)[0];
}
var foo = 'hello';
var output = getVariableName({ foo }); // Note the curly brackets
console.log(output);
This works because you created a new object with key foo and value the same as the variable foo, in this case hello. Then our helper method gets the first key as a string.
Credit goes to this tweet.
Converting a set of unique variable into one JSON object for which I wrote this function
function makeJSON(){ //Pass the variable names as string parameters [not by reference]
ret={};
for(i=0; i<arguments.length; i++){
eval("ret."+arguments[i]+"="+arguments[i]);
}
return ret;
}
Example:
a=b=c=3;
console.log(makeJSON('a','b','c'));
Perhaps this is the reason for this query
I think you can use
getVariableName({foo});
Use a 2D reference array with .filter()
Note: I now feel that #Offermo's answer above is the best one to use. Leaving up my answer for reference, though I mostly wouldn't recommend using it.
Here is what I came up with independently, which requires explicit declaration of variable names and only works with unique values. (But will work if those two conditions are met.)
// Initialize some variables
let var1 = "stick"
let var2 = "goo"
let var3 = "hello"
let var4 = "asdf"
// Create a 2D array of variable names
const varNames = [
[var1, "var1"],
[var2, "var2"],
[var3, "var3"]
]
// Return either name of variable or `undefined` if no match
const getName = v => varNames.filter(name => name[0] === v).length
? varNames.filter(name => name[0] === v)[0][1]
: undefined
// Use `getName` with OP's original function
function getVariableName(unknownVariable){
return getName(unknownVariable)
}
This is my take for logging the name of an input and its value at the same time:
function logVariableAndName(unknownVariable) {
const variableName = Object.keys(unknownVariable)[0];
const value = unknownVariable[variableName];
console.log(variableName);
console.log(value);
}
Then you can use it like logVariableAndName({ someVariable })

Categories