I'm trying to see if array.reduce would be a better option in this case.
I'd like to return the string result instead of having to set a variable in the forEach loop.
So what I'm doing is seeing if there are any matches inside a string matching my regex. Then getting the text inside that match and replacing it with a variable I pass to it. All works great but I'd like to clean it up if I can. findReplaceTemplateString is a separate function and I'm ok with that. Its the forEach feels like I could use a reducer instead to return the completed string. But I'm new to reducers and not sure if this is a good case for it. Anyone have any thoughts on this.
const reg = /\{.+?\}/g;
const matches = tpl.match(reg);
let str = '';
matches.map(item => item.slice(1, -1)).forEach((placeHolder) => {
str = findReplace(tpl, placeHolder, props[placeHolder] || '');
});
I don't see the point of overcomplicating it. Simply use String.prototype.replace() with function as a second parameter. That will dynamically replace your pattern with valid parameters.
const input = 'Hi there, {name}! What are you doing in {city}?';
const props = {name: 'Alex', city: 'St Petersburg'};
const output = input.replace(/\{.+?\}/g, (p) => {
return props[p.slice(1, -1)] || p /* here you may use '' */;
});
console.log( output );
Related
This is something I still don't quite understand very well, I have the following situation:
// #2) Check if this array includes any name that has "John" inside of it. If it does, return that
// name or names in an array.
const dragons = ['Tim', 'Johnathan', 'Sandy', 'Sarah'];
at first I tried the following:
const name = dragons.forEach(element => {
element.includes("John") ? element : null
});
it returns undefined
then:
const name = dragons.filter(element => {
element.includes("John");
});
it returns an empty array
then:
function name() {
const dragons = ['Tim', 'Johnathan', 'Sandy', 'Sarah'];
dragons.forEach(element => {
if (element.includes("John")) {
return element;
}
});
}
again it returns undefined
but the interesting thing is that if on any of the attempts I change the action to do to console.log(element); then it will show "Johnathan" which is the correct output.
so it means that the logic is working, I just don't understand why it won't return the value if I want to let's say assign it to a variable.
I even tried the following:
const dragons = ['Tim', 'Johnathan', 'Sandy', 'Sarah'];
dragons.forEach(element => {
if (element.includes("John")) {
const name = element;
}
});
but it again returns name as undefined, why is that?
edit: this was my first stack overflow question, really I wasn't expecting someone answering my question, I thought maybe it was a dumb question or that i didn't explained myself well but it was very nice to find such a supportive community, thank you so much to all of you who commented and helped me!
forEach method does return undefined. But filter instead returns the array but filtered. However, the problem is that you are forgetting to return the values inside it.
To fix it try:
const name = dragons.filter(element => {
return element.includes("John");
});
Or:
const name = dragons.filter(element => element.includes("John"));
First, forEach never returns anything. You'd need a map for that, or filter (what are you exactly after?). Second, your callback functions don't return anything, either. You can map or filter your array like that:
const dragonsNamedJohn = dragons.filter(name => name.includes('John'));
or
const dragonsNamedJohn = dragons.filter(name => {
return name.includes('John');
});
or even
function hisNameIsJohn (name) {
return name.includes('John');
}
const dragonsNamedJohn = dragons.filter(hisNameIsJohn);
(You might want to stick toLowerCase() somewhere there, or not, dependent on whether you want the comparison to be case-sensitive. Also, another way of skinning a dragon would be to use a regular expression).
const example: (string: string) => string = string => {
return string.split("");
}
Its not a detailed question - I know, but I couldnt think of another way to ask and I simply do not understand what is happening in the below code.
I only understand only string as type is accepted, but how is this even a function -shouldnt it be:
const example = (string:string) => return string.split("")}
instead ?
Thanks!!
UPDATE:
There was some miscommunication maybe. I only want to understand the following (in bold letters):
const example**: (string: string) => string** = string => {
return string.split("");
}
why is it ":" and not "="?
what is: => string = string =>
why not just = string =>
?
Okay, actually there's a type error, it should be:
const example: (string: string) => string[] = string => {
return string.split("");
}
instead of (string: string) => string because string.split() will return an array.
also, it's better to rename the argument to something other than string as it can be easily confused with the type string.
Here's a fixed version:
const example: (someArg: string) => string[] = (someString) => {
return someString.split('');
};
So, the function example simply takes an argument (of type string) and returns an array of strings that is indicated by (string: string) => string[].
You can also use the below syntax if you find above a bit confusing:
const example2 = (someString: string): string[] => {
return someString.split('');
};
Update:
Considering this (string: string) => string,
why is it ":" and not "="?
It is not = because we're not yet assigning the value. We're still in the process of assigning the type. After giving the type, = is used to finally give it a value (which is an arrow function).
what is: => string = string => why not just = string => ?
TypeScript does understand that the returned value is an array of strings but => string[] is used to explicitly mention the return type (to make sure that the function always returns what we want it to return), we use => to separate the argument types and return type. Think of this as an arrow function like syntax for giving the type to a function.
This is the type:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
const example: (someArg: string) => string[] = (someString) => {
return someString.split('');
};
A function taking a string and returning a string.
Everything after the equals sign is the value.
You can use arrow function methodology in typescripts. It is a common way in most programming language for make more easy to read.
It is just a syntax. In background typescript will convert this method to vanilla function version.
your above code look like below code. There are many ways to write the same method different versions.
// you can write like this
function example( string: string ){
return string.split("");
}
// If you need to write multiple lines you should use circle brackets
const example = ( str : string ) => {
// do something here...
return str.split("");
}
// if you don't need to write multiple lines you can return like this.
const example = ( str : string ) => str.split("");
// But If you want to use return syntax. you can do. it is up to you.
const example = ( str : string ) => return str.split("");
If you give more detail. I can help you and apologize for my english.
If I misunderstand sorry about that.
I'm learning javascript and I have a problem I stumbled upon. I have the following javascript code (I'm searching for the name john inside MyArray, which contains class objects):
MyArray.forEach(
(v,i)=>(
v.name == 'john' && return i
)
);
This is not working, because I get the following error for the 3rd line:
SyntaxError: expected expression, got keyword 'return'
My question is, how can I put statement inside an expression?
How can i put statement inside an expression?
You can't, currently. Nor, in that example, do you want to. The return value of the forEach callback isn't used for anything.
See below the horizontal rule for your specific case, but answering the question you actually asked:
On occasion, wanting to do some statement logic and then have an overall expression take the final result of that statement logic does come up. Consequently, there's a proposal for do expressions working its way through the JavaScript proposals process. It's only at Stage 1 of the process, and so it may not progress, or if it progresses the form of it may change, but for now it looks like this:
let x = do { // Using the `do` expression proposal
let tmp = f(); // (currently just Stage 1, do not use!)
tmp * tmp + 1
};
...where x would get the result of that last expression within the {}.
If you really wanted to do that now, you'd probably use an immediately-invoked arrow function:
let x = (() => {
let tmp = f();
return tmp * tmp + 1;
})();
The purpose of do expression proposal is to make that less verbose, when you need to do.
(Please don't mistake this for my offering an opinion on do expressions either way. I'm just noting the proposal exists and is working through the process.)
Your specific case of finding the index of an item:
In your specific case, you're looking for the Array#findIndex function, which returns the index of the entry where the callback returns a truthy value:
const index = MyArray.findIndex(v => v.name == 'john');
(Or you might want Array#find if you want the entry itself instead of its index.)
Note that since that uses a concise arrow function, the return value of the function is the value of the expression forming the function body. We could use a verbose one instead with an explicit return:
const index = MyArray.findIndex(v => { return v.name == 'john'; });
Looks like you want to find the index of the Mr. John? Then use dedicated method findIndex for this:
const john = MyArray.findIndex(
(v,i) => (
v.name == 'john'
)
);
You cant. Just do:
for(const [index, el] of myArray.entries())
if(el.name === "john") return index;
Or more easy:
return myArray.findIndex(el => el.name === "john");
forEach is just a loop of all elements. You can't return anything there like you did. You could use find for example, if you want the object with the name john:
var MyArray = [
{name: 'test', some: 'data'},
{name: 'john', some: 'data'},
];
var obj = MyArray.find(v => v.name == 'john');
console.log(obj);
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 = "")
I have the following situation:
There is a certain string that contains some vars, such as:
var string = '/page/{id}/{title}';
Now, I want to be able to replace {id} and {title} with the vars from the following array:
var arr = {'id':10, 'title':'test-page'};
I came up with this little regex:
string.replace ( /{([a-zA-Z0-9]+)}/g , '$1' );
Which, as expected, just returns this:
/page/id/title
So I tried this:
string.replace ( /{([a-zA-Z0-9]+)}/g , arr [ '$1' ] );
But that returns a
/page/undefined/undefined
Now, I understand that something like this would be possible with a loop et cetera, but it would be nice to have a one-liner for this. I am not very used to JS, so I hope that there is some function or option that I am unaware of that helps me out with this :).
Best regards!
Try something like this:
var arr = {'id':10, 'title':'test-page'};
'/page/{id}/{title}'.replace(/\{([\w\d]+?)\}/g, function(a, b) {
return arr[b] || '';
});
If you use this replace thing often I would create a String helper prototype method. For example:
String.prototype.template = function(data) {
return this.replace(/\{([\w\d]+?)\}/g, function(a, b) {
return data[b] || '';
});
};
and use it like this:
'/page/{id}/{title}'.template(arr);
According to MDN article,
Because we want to further
transform the result of the match before the final substitution is
made, we must use a function. This forces the evaluation of the match
prior to the toLowerCase() method. If we had tried to do this using
the match without a function, the toLowerCase() would have no effect.
(In the text above, replace toLowerCase() with "accessing property in object")
Then, you can use
function replacer(match, p1, p2, p3/*, ...*/, offset, string){
return arr[p1];
}
var arr = {'id':10, 'title':'test-page'};
'/page/{id}/{title}'.replace(/\{([\w\d]+?)\}/g, replacer);