Let say we want to fix text that was written on a AZERTY(or Dvorak, or another language layout) keyboard, as if it was a regular QWERTY one. We want to replace certain characters on corresponding ones.
So we have a dictionary, that looks similar to this:
const dic = {
q: a,
z: w,
}
But the dictionary is big, for every character. I'm searching for a way to do this not using for loops, using advantages of latest javascript changes, maybe with .replace() or .map() or anything you can suggest.
You don't need a big dictionary since a lot of the keys on AZERTY are in the same place: only make a map for the keys that are different and return the input if no mapping exists:
const dict = {
q: "a",
w: "z"
// e, r, t, y are in the same place, for example...
};
console.log(
"qwerty".split("") // Explode the string to an array
.map(letter => dict[letter] || letter) // Map inputs
.join("") // output a string
);
// Generate a reverse mapping
const reverse_dict = {};
Object.keys(dict).forEach(key => reverse_dict[dict[key]] = key);
console.log(
"azerty".split("")
.map(letter => reverse_dict[letter] || letter)
.join("")
);
Note that with arrow functions:
You do not need parenthesis around the parameter list if there is only one parameter.
If you omit the {} around the function body, you can also omit the return keyword and the body will return the value of the last (only) expression (save for a void parameter, which is a set of empty brackets () which you'll see in places like setTimeout).
A common misconception about const is that you can't update an array or object after it's initialized, but that's not the case. What you can't do is assign the variable to something else (like const o = {}; o = "")
Related
I would like to take text that I generated and stored in a string and use it like a template literal.
var generatedText = "Pretend this text was generated and then stored in a variable. ";
generatedText = "But I still need to use it as a template it to get ${variable}.";
var variable = "Successs!!!!";
console.log(generatedText);
//prints 'But I still need to interpolate it to get ${variable}.'
//how can I make it print using variable in it like a template as if it were doing this
console.log(`But I still need to use it as a template it to get ${variable}.`);
//prints 'But I still need to use it as a template it to get Successs!!!!.'
How can I get generated text to become a template string?
generatedText must start in a variable so I need to find a way to convert it to a template string if possible.
Edit:
I didn't think I would have to put this but also I don't want to use eval to risk evaluating random code...
For the general situation, you can use a replacer function to replace every occurrence of ${someProp} with the someProp property on an object:
const interpolate = (str, obj) => str.replace(
/\${([^}]+)}/g,
(_, prop) => obj[prop]
);
const generatedText = "But I still need to use it as a template it to get ${variable}.";
const variable = "Successs!!!!";
console.log(interpolate(generatedText, { variable }));
The regular expression \${([^}]+)} means:
\$ - Literal $
{ - Literal {
([^}]+) First (and only) capture group:
[^}]+ - One or more characters which are not }
} - Literal }
Since prop is the property name found in between the brackets, replace with obj[prop] to replace with the desired replacement.
The interpolate function below is an extended version of this answer that adds support for simple nested object field references (e.g.: a.b.c)
function interpolate(s, obj) {
return s.replace(/[$]{([^}]+)}/g, function(_, path) {
const properties = path.split('.');
return properties.reduce((prev, curr) => prev && prev[curr], obj);
})
}
console.log(interpolate('hello ${a.b.c}', {a: {b: {c: 'world'}}}));
// Displays 'hello world'
The interpolate function below is an extended version of the above solution adds support for simple nested object field references, with addition of arrays (e.g.: a[0][2].b.c)
const interpolate = (str, obj) => {
return str.replace(/\${([^}]+)}/g, (_, target) => {
let keys = target.split(".");
return keys.reduce((prev, curr) => {
if (curr.search(/\[/g) > -1) {
//if element/key in target array is array, get the value and return
let m_curr = curr.replace(/\]/g, "");
let arr = m_curr.split("[");
return arr.reduce((pr, cu) => {
return pr && pr[cu];
}, prev);
} else {
//else it is a object, get the value and return
return prev && prev[curr];
}
}, obj);
});
};
let template = "hello ${a[0][0].b.c}";
let data = {
a: [
[{
b: {
c: "world",
f: "greetings"
}
}, 2], 3
],
d: 12,
e: 14
}
console.log(interpolate(template, { ...data
}));
You should emulate a template literal instead, because letting text from ~somewhere~ run arbitrary JavaScript like a real template literal’s ${} sections can usually isn’t a good idea:
generatedText.replace(/\$\{variable}/g, variable);
I would like to take text that I generated and stored in a string and use it like a template literal.
var generatedText = "Pretend this text was generated and then stored in a variable. ";
generatedText = "But I still need to use it as a template it to get ${variable}.";
var variable = "Successs!!!!";
console.log(generatedText);
//prints 'But I still need to interpolate it to get ${variable}.'
//how can I make it print using variable in it like a template as if it were doing this
console.log(`But I still need to use it as a template it to get ${variable}.`);
//prints 'But I still need to use it as a template it to get Successs!!!!.'
How can I get generated text to become a template string?
generatedText must start in a variable so I need to find a way to convert it to a template string if possible.
Edit:
I didn't think I would have to put this but also I don't want to use eval to risk evaluating random code...
For the general situation, you can use a replacer function to replace every occurrence of ${someProp} with the someProp property on an object:
const interpolate = (str, obj) => str.replace(
/\${([^}]+)}/g,
(_, prop) => obj[prop]
);
const generatedText = "But I still need to use it as a template it to get ${variable}.";
const variable = "Successs!!!!";
console.log(interpolate(generatedText, { variable }));
The regular expression \${([^}]+)} means:
\$ - Literal $
{ - Literal {
([^}]+) First (and only) capture group:
[^}]+ - One or more characters which are not }
} - Literal }
Since prop is the property name found in between the brackets, replace with obj[prop] to replace with the desired replacement.
The interpolate function below is an extended version of this answer that adds support for simple nested object field references (e.g.: a.b.c)
function interpolate(s, obj) {
return s.replace(/[$]{([^}]+)}/g, function(_, path) {
const properties = path.split('.');
return properties.reduce((prev, curr) => prev && prev[curr], obj);
})
}
console.log(interpolate('hello ${a.b.c}', {a: {b: {c: 'world'}}}));
// Displays 'hello world'
The interpolate function below is an extended version of the above solution adds support for simple nested object field references, with addition of arrays (e.g.: a[0][2].b.c)
const interpolate = (str, obj) => {
return str.replace(/\${([^}]+)}/g, (_, target) => {
let keys = target.split(".");
return keys.reduce((prev, curr) => {
if (curr.search(/\[/g) > -1) {
//if element/key in target array is array, get the value and return
let m_curr = curr.replace(/\]/g, "");
let arr = m_curr.split("[");
return arr.reduce((pr, cu) => {
return pr && pr[cu];
}, prev);
} else {
//else it is a object, get the value and return
return prev && prev[curr];
}
}, obj);
});
};
let template = "hello ${a[0][0].b.c}";
let data = {
a: [
[{
b: {
c: "world",
f: "greetings"
}
}, 2], 3
],
d: 12,
e: 14
}
console.log(interpolate(template, { ...data
}));
You should emulate a template literal instead, because letting text from ~somewhere~ run arbitrary JavaScript like a real template literal’s ${} sections can usually isn’t a good idea:
generatedText.replace(/\$\{variable}/g, 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} !!!`)
I have an application that can turns a tex file into a JavaScript object, with key-value pairs. The key being the word and the value being the number of times it has appeared in the text file. Let's go through it together:
FormatText.prototype.toDowncase = function() {
return this._data = this._data.toLowerCase();
};
This turns the words to lowercase
FormatText.prototype.deleteWords = function() {
return this._data = this._data.replace(/\W/g, " ");
};
This replaces all non-words with a space
FormatText.prototype.splitWords = function() {
return this._data = this._data.split(/\s+/);
};
This turns the string in an array and splits at each delimiter
FormatText.prototype.filterEntries = function() {
return this._data = this._data.filter(v => !!v);
};
This one above I have no clue what it does.
FormatText.prototype.countWords = function() {
return this._data = this._data.reduce((dict, v) => {dict[v] = v in dict ? dict[v] + 1 : 1; return dict}, {});
}
Could someone explain this one, however I will get it a try:
This one takes the array and passed the method 'reduce' with two arguments. It counts how many times each individual word has appeared and returns an object with the 'key-value' pairs described at the beginning of this question.
v => !!v means take v, and coerce it to a Boolean type by applying NOT twice. So the filter function is basically removing any falsey values (0, null, undefined) from this._data.
countWords is counting the number of times each word occurs in this._data - it is going through the array and adding 1 to the count if the word has been encountered before, or returning 1 if the word has not been encountered before. It returns an object with the words as keys and the counts as values.
As a note, these functions change the type of this._data, from a string, to an array, to an object. That may cause bugs to appear if e.g. you run the same method twice
Why not just return the value, without NOT NOT, like
v => v
because for filtering the value coerces to a boolean value.
From Array#filter:
Description
filter() calls a provided callback function once for each element in an array, and constructs a new array of all the values for which callback returns a value that coerces to true. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values. Array elements which do not pass the callback test are simply skipped, and are not included in the new array.
In this case the double exclamation mark is useless: the value returned from the callback in filter(callback) is then coerced to a boolean automatically, so no need to do it using double exclamation mark. The following lines are equivalent:
.filter(v => !!v)
.filter(v => v)
.filter(Boolean)
This one above I have no clue what it does.
The javascript operator ! (logical not) performs a type coercion (to boolean) on its argument. So applied twice you somehow convert any type to a boolean value which gives you whether it is falsy or truthy.
This is interesting when you want to apply a condition to different types whose semantic is more or less "no value". For example:
!!('') //false
!!(0) //false
!!null //false
!!undefined //false
Could someone explain this one, however I will get it a try
reduce is method of the array prototype which allows to iterate over a collection while aggregating value.
In your specific example the aggregator is a dictionary which maps a word to a count (number of appearance). So if the word is not present in the dictionary it creates a key for this word with a counter initialized to 1 otherwise it increments the counter (if word already present).
A equivalent could be
const countWords = function (words = [], dictionary = {}) {
if(words.length === 0) {
return dictionary;
}
const word = words.pop(); //remove and read the word at the end of the array
if(word in dictionary) {//if key is present in the dictionary
dictionary[word] += 1; // increment
else {
dictionary[word] = 1; // start a counter for new keyword
}
return countWords(words, dictionary);
}
Is there a simplified way in JavaScript + Reg-Ex to replace all occurrences of each symbol in a set with its alternative symbol?
Example:
"Test: 1, 2, 3 and 1".replace("123", "ABC");
// we need to get: "Test: A, B, C and A";
I mean, is there any Reg-Ex trick to avoid a for-loop here? And anyway, what would be the most efficient way to do it?
Provisions:
In my task the set of symbols is usually very short (could even be 3 symbols) and it is static, not dynamic, very similar to the example shown.
Sometimes a symbol needs to be replaced with an empty '', i.e. removed.
You could build out your replacements using an Object Literal.
var map = {'1':'A', '2':'B', '3':'C'};
str = str.replace(/[123]/g, function(k) {
return map[k];
});
Or create a custom function using a map for this:
function _replace(str, map) {
var re = new RegExp('['+Object.keys(map).join('')+']','g');
return str.replace(re, function(x) { return map[x] });
}