I have a string with say: My Name is %NAME% and my age is %AGE%.
%XXX% are placeholders. We need to substitute values there from an object.
Object looks like: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}
I need to parse the object and replace the string with corresponding values. So that final output will be:
My Name is Mike and my age is 26.
The whole thing has to be done either using pure javascript or jquery.
The requirements of the original question clearly couldn't benefit from string interpolation, as it seems like it's a runtime processing of arbitrary replacement keys.
However, if you just had to do string interpolation, you can use:
const str = `My name is ${replacements.name} and my age is ${replacements.age}.`
Note the backticks delimiting the string, they are required.
For an answer suiting the particular OP's requirement, you could use String.prototype.replace() for the replacements.
The following code will handle all matches and not touch ones without a replacement (so long as your replacement values are all strings, if not, see below).
var replacements = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"},
str = 'My Name is %NAME% and my age is %AGE%.';
str = str.replace(/%\w+%/g, function(all) {
return replacements[all] || all;
});
jsFiddle.
If some of your replacements are not strings, be sure they exists in the object first. If you have a format like the example, i.e. wrapped in percentage signs, you can use the in operator to achieve this.
jsFiddle.
However, if your format doesn't have a special format, i.e. any string, and your replacements object doesn't have a null prototype, use Object.prototype.hasOwnProperty(), unless you can guarantee that none of your potential replaced substrings will clash with property names on the prototype.
jsFiddle.
Otherwise, if your replacement string was 'hasOwnProperty', you would get a resultant messed up string.
jsFiddle.
As a side note, you should be called replacements an Object, not an Array.
How about using ES6 template literals?
var a = "cat";
var b = "fat";
console.log(`my ${a} is ${b}`); //notice back-ticked string
More about template literals...
Currently there is still no native solution in Javascript for this behavior. Tagged templates are something related, but don't solve it.
Here there is a refactor of alex's solution with an object for replacements.
The solution uses arrow functions and a similar syntax for the placeholders as the native Javascript interpolation in template literals ({} instead of %%). Also there is no need to include delimiters (%) in the names of the replacements.
There are two flavors (three with the update): descriptive, reduced, elegant reduced with groups.
Descriptive solution:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(
/{\w+}/g,
placeholderWithDelimiters => {
const placeholderWithoutDelimiters = placeholderWithDelimiters.substring(
1,
placeholderWithDelimiters.length - 1,
);
const stringReplacement = replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters;
return stringReplacement;
},
);
console.log(string);
Reduced solution:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(/{\w+}/g, placeholder =>
replacements[placeholder.substring(1, placeholder.length - 1)] || placeholder
);
console.log(string);
UPDATE 2020-12-10
Elegant reduced solution with groups, as suggested by #Kade in the comments:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '26',
};
const string = stringWithPlaceholders.replace(
/{(\w+)}/g,
(placeholderWithDelimiters, placeholderWithoutDelimiters) =>
replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters
);
console.log(string);
UPDATE 2021-01-21
Support empty string as a replacement, as suggested by #Jesper in the comments:
const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';
const replacements = {
name: 'Mike',
age: '',
};
const string = stringWithPlaceholders.replace(
/{(\w+)}/g,
(placeholderWithDelimiters, placeholderWithoutDelimiters) =>
replacements.hasOwnProperty(placeholderWithoutDelimiters) ?
replacements[placeholderWithoutDelimiters] : placeholderWithDelimiters
);
console.log(string);
You can use JQuery(jquery.validate.js) to make it work easily.
$.validator.format("My name is {0}, I'm {1} years old",["Bob","23"]);
Or if you want to use just that feature you can define that function and just use it like
function format(source, params) {
$.each(params,function (i, n) {
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
})
return source;
}
alert(format("{0} is a {1}", ["Michael", "Guy"]));
credit to jquery.validate.js team
As with modern browser, placeholder is supported by new version of Chrome / Firefox, similar as the C style function printf().
Placeholders:
%s String.
%d,%i Integer number.
%f Floating point number.
%o Object hyperlink.
e.g.
console.log("generation 0:\t%f, %f, %f", a1a1, a1a2, a2a2);
BTW, to see the output:
In Chrome, use shortcut Ctrl + Shift + J or F12 to open developer tool.
In Firefox, use shortcut Ctrl + Shift + K or F12 to open developer tool.
#Update - nodejs support
Seems nodejs don't support %f, instead, could use %d in nodejs.
With %d number will be printed as floating number, not just integer.
Just use replace()
var values = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
var substitutedString = "My Name is %NAME% and my age is %AGE%.".replace("%NAME%", $values["%NAME%"]).replace("%AGE%", $values["%AGE%"]);
You can use a custom replace function like this:
var str = "My Name is %NAME% and my age is %AGE%.";
var replaceData = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
function substitute(str, data) {
var output = str.replace(/%[^%]+%/g, function(match) {
if (match in data) {
return(data[match]);
} else {
return("");
}
});
return(output);
}
var output = substitute(str, replaceData);
You can see it work here: http://jsfiddle.net/jfriend00/DyCwk/.
If you want to do something closer to console.log like replacing %s placeholders like in
>console.log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>Hello Loreto how are you today is everything allright?
I wrote this
function log() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(1, args.length);
var i=0;
var output = args[0].replace(/%s/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return(output);
}
res=log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright");
document.getElementById("console").innerHTML=res;
<span id="console"/>
you will get
>log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>"Hello Loreto how are you today is everything allright?"
UPDATE
I have added a simple variant as String.prototype useful when dealing with string transformations, here is it:
String.prototype.log = function() {
var args = Array.prototype.slice.call(arguments);
var rep= args.slice(0, args.length);
var i=0;
var output = this.replace(/%s|%d|%f|%#/g, function(match,idx) {
var subst=rep.slice(i, ++i);
return( subst );
});
return output;
}
In that case you will do
"Hello %s how are you %s is everything %s?".log("Loreto", "today", "allright")
"Hello Loreto how are you today is everything allright?"
Try this version here
This allows you to do exactly that
NPM: https://www.npmjs.com/package/stringinject
GitHub: https://github.com/tjcafferkey/stringinject
By doing the following:
var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });
// My username is tjcafferkey on Git
I have written a code that lets you format string easily.
Use this function.
function format() {
if (arguments.length === 0) {
throw "No arguments";
}
const string = arguments[0];
const lst = string.split("{}");
if (lst.length !== arguments.length) {
throw "Placeholder format mismatched";
}
let string2 = "";
let off = 1;
for (let i = 0; i < lst.length; i++) {
if (off < arguments.length) {
string2 += lst[i] + arguments[off++]
} else {
string2 += lst[i]
}
}
return string2;
}
Example
format('My Name is {} and my age is {}', 'Mike', 26);
Output
My Name is Mike and my age is 26
Another solution if you're using node.js is StackExchange's own formatUnicorn utility function (https://www.npmjs.com/package/format-unicorn):
let x = {name:'jason', food:'pine cones'};
let s = '{name} enjoys a delicious bowl of {food}';
let formatted = x.formatUnicorn(s);
Also, a bit of an edge case, but if you aren't using Node but you do just happen to be writing a userscript for SE sites, then formatUnicorn will already be on the String prototype.
As a quick example:
var name = 'jack';
var age = 40;
console.log('%s is %d yrs old',name,age);
The output is:
jack is 40 yrs old
Here is another way of doing this by using es6 template literals dynamically at runtime.
const str = 'My name is ${name} and my age is ${age}.'
const obj = {name:'Simon', age:'33'}
const result = new Function('const {' + Object.keys(obj).join(',') + '} = this.obj;return `' + str + '`').call({obj})
document.body.innerHTML = result
const stringInject = (str = '', obj = {}) => {
let newStr = str;
Object.keys(obj).forEach((key) => {
let placeHolder = `#${key}#`;
if(newStr.includes(placeHolder)) {
newStr = newStr.replace(placeHolder, obj[key] || " ");
}
});
return newStr;
}
Input: stringInject("Hi #name#, How are you?", {name: "Ram"});
Output: "Hi Ram, How are you?"
ES6:
const strFormat = (str, ...args) => args.reduce((s, v) => s.replace('%s', v), str);
// Use it like:
const result = strFormat('%s is %s yrs old', 'name', 23);
Lots of good/similar answers here. I wanted the ability to easily get a nested key in an object (or perhaps some JSON data structure) for substitution, so I took the following simple approach:
const getKey = (d, path) => {
// path can be a string like 'key1.key2' or an iterable of keys
if (typeof(path) === 'string') {
path = path.split('.')
}
return path.reduce((x, y) => x[y], d)
}
const inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> getKey(obj, g));
// Example
> const str = 'there are ${a} ways to ${b.c}'
undefined
> inject(str, {'a':'many', 'b': {'c': 'skin a cat'}})
'there are many ways to skin a cat'
Some inspiration from this and this.
This is a merged solution of Gerson Diniz and Shubham Vyas.
It is possible to pass a set of arguments or an object.
function strSwap(str) {
if (!str) return null;
let args = [];
for (let a of arguments)
args.push(a);
args.shift();
if (!args.length) return null;
// replacement by object - {{prop}}
if (!!(args[0].constructor && args[0].constructor.name.toLowerCase() === 'object')) {
for (let i in args[0]) {
let n = `{{${i}}}`;
str = str.includes(n) ? str.replaceAll(n, args[0][i] + '') : str;
}
}
// replacement by placeholders - %s
else {
str = args.reduce((s, v) => s.replace('%s', v), str);
}
return str;
}
// ---------------------
console.log(strSwap('Hello %s, my name is %s.', 'alice', 'bob'));
console.log(strSwap('Hello {{a}}, my name is {{b}}. Hello {{b}}.', {
a: 'alice',
b: 'bob'
}));
I am trying to learn how to use filter() using javascript with strings. My idea is to use filter() to find all occurrences that has the same string value in an array and and use those that to print out all the names that has the same grade from another array.
But I have this far only been able to print all the student's name with the respectively grade instead of for example only all names who has grade 'C'.
What I have tried this far is to input the value/grade into a textbox so I should can use that values to be searched for in the array. I have implemented a basic code that should be able to solve my the mission of this code. But nothing happens unless I use a for loop, then it prints all names with their respectively grade. And as far as I have understood from what I have been reading I should not need to use any for loop to get the code to do what I want.
I am feeling stuck.
I assume that it depends on that I have not been able find any examples that uses strings that must much as all I have found this far is example that uses checks string values after their length and not for the conditions I want to make the code for.
I might have missed the information I need, but This far I have not seen anything close to what might help me understand how to solve this problem.
this is the style of information of some source that I have found and everything else I found this far has been very similar to these.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
https://www.geeksforgeeks.org/javascript-array-filter
https://www.javascripttutorial.net/javascript-array-filter/
I am not sure if I should show any code or not but here it goes anyway.
var studentArray = ["Clara", "Andreas", "Emilio", "Sarah", "Alice", "Victoria", "Thomas", "Roberta"];
var gradeArray = ["A", "B", "C", "A", "D", "C", "E", "E"];
function input() {
var grade = document.getElementById("grade").value;
return grade;
}
function getGradesToNames() {
var filteredArray = gradeArray.filter(input);
for (var i = 0; i < filteredArray.length; i++){
document.getElementById("output2").innerHTML += studentArray[i] + " " + filteredArray[i] + "<br/>";
}
}
function print() {
input();
getGradesToNames();
}
I know I am a complete beginner but I hope someone can help me by guiding me in the right direction.
Thank you in advance.
Using .filter won't work well here, at least not alone, because once you've filtered one of the arrays, you'll have lost the index of each item that connects it to the item in the other array. If you want to filter, zip the arrays together first, so you have an array of objects with name and grade properties:
var studentArray = ["Clara", "Andreas", "Emilio", "Sarah", "Alice", "Victoria", "Thomas", "Roberta"];
var gradeArray = ["A", "B", "C", "A", "D", "C", "E", "E"];
const zipped = studentArray.map((name, i) => ({ name, grade: gradeArray[i] }));
function getGradesToNames() {
zipped
.filter(({ grade }) => grade === 'C')
.forEach(({ name, grade }) => {
document.getElementById("output2").innerHTML += name + " " + grade + "<br/>";
});
}
getGradesToNames();
<div id="output2"></div>
If the grade to be searched for comes from a variable, like an input, just substitute the === 'C' with a comparison against that variable.
I am trying to make a small random quizz generator, first array has the questions and they are randomly displayed (when page is loaded or when page is refreshed), the second array has the answers.
Now, when the user typed an answer, if that answer is from the answers' array is shown the correct message, even if it is not correct one for that question, and if the answer is not from the answers' array, the not correct message is shown.
I need a code to solve this (I can do it in if...else and with || and && operators, but for more than 5 entries code becomes too long and too hard to maintain)
Below javascript and html codes
//reload whole page
function refreshMe() {
window.location='index.html';
}
const myQs = [
"How many sides has a hexagon", // must always be the first answer from myRs array
"A circle has...degrees?", // must always be the second answer from myRs array
"2^3=...?",
"2+2:2=..?",
"A triangle has....degrees?",
"Square root of 2 is...?" // to be extend up to 500 entries
];
let randomItem = myQs[Math.floor(Math.random()*myQs.length)];
document.getElementById("demo").innerHTML = randomItem;
function userAnswer() {
const check = document.getElementById('answer').value;
const myRs = [
"6",
"360",
"8",
"3",
"180",
"1.41"
];
// the difference between 0 and -1?
if (myRs.indexOf(check) > -1) {
sayMessage = check + " is the correct answer!";
} else {
sayMessage = check + " is not the correct answer....";
}
document.getElementById("userA").innerHTML = sayMessage;
};
For a random question, now every answer is correct, if a answer out of myRs is typed, the message is not correct. I need a code so that the questions from myQs array to match with its own answer from myRs array, the same index in arrays, first question has first answer, and so on.
I can do it with if...else and || and && operators, but for more than 5 entries the code get too long and too hard for maintaining it.
<p> The question of the moment...</p>
<p id="demo"></p>
<button onclick="refreshMe()">Next one </button><br><br>
<input name="answer" type="text" placeholder="Your answer is....." id="answer">
<br><br>
<button onclick="userAnswer()">Check answer</button>
<p id="userA"></p>
Right now you have two lists. One contains questions, the other answers:
var questions = [ "2 + 2", "2 + 1", "1 + 1" ];
var answers = [ "4", "3", "2" ];
In order to easily check your user input, it's a good idea to convert this data structure to an object in which the question is the key, and the answer the value.
Note that this requires your question and answer combinations to be unique. I.e.: a question can not have multiple answers
var questionsWithAnswers = {
"2 + 2": "4",
// etc.
}
You can convert the data in javascript, so you don't have to rewrite the lists:
var questionsWithAnswers = {};
for (let i = 0; i < questions.length; i += 1) {
questionsWithAnswers[question[i]] = answers[i];
}
Now, your check for a question is:
var answerIsCorrect = questionsWithAnswers[question] === userInput;
I'd recommend revising the schema of your questions.
Housing questions and answers together within objects would probably be the least error prone approach.
See below for a practical example.
// Questions.
const questions = [
{q: 'How may sidea has a hexagon', a: 6},
{q: 'A circle has...degrees?', a: 360},
{q: '2^3=...?', a: 8},
{q: '2+2:2=..?', a: 3},
{q: 'A triangle has....degrees?', a: 180},
{q: 'Square root of 2 is...?', a: 1.41}
]
// Selected.
const selected = questions[Math.floor(Math.random()*questions.length)]
// Question.
const question = selected.q
document.getElementById('question').innerHTML = question
// Normalise.
const normalise = (number) => Math.round(parseFloat(number), 2)
// Submit Answer.
document.getElementById('form').addEventListener('submit', (event) => {
event.preventDefault()
const userAnswer = normalise(document.getElementById('answer').value)
const actualAnswer = normalise(selected.a)
const isCorrect = (userAnswer == actualAnswer)
document.getElementById('feedback').innerHTML = isCorrect && 'Correct 👍' || 'Incorrect 👎'
})
<div><p id="question"></p></div>
<form id="form">
<div><input id="answer" placeholder="Answer .."/></div>
<button>Submit answer</button>
</form>
<div><p id="feedback"></p><div>
First of all, you do understand that having a question and answer available in JS is not a good idea, cause user can see the code.
Anyway, I would combine questions and answer into one structure.
var QAs = [["How many sides has a hexagon", "6"],
["A circle has...degrees?", "360"]]; // and so on
let randomItem = QAs[Math.floor(Math.random()*myQs.length)];
document.getElementById("demo").innerHTML = randomItem[0];
function userAnswer() {
const check = document.getElementById('answer').value;
if (check == randomItem[1]) { // check randomItem availability in this scope. otherwise save to to window.randowItem scope.
sayMessage = check + " is the correct answer!";
} else {
sayMessage = check + " is not the correct answer....";
}
document.getElementById("userA").innerHTML = sayMessage;
}