I need to replace two strings using regular expression value replacement so the resulting string is $?tlang=es&text=Hello world, so I didn't know to use here String.prototype.replace().
const value = "Hello world"
const queryString = "?tlang=es&text=$1"
In this scenary, value and queryString are hard-coded, but in "real life" it should be the result of a regular expression group capturing like line.match(/msgid \"(.*)\"/) where line is an iterated text line and queryString is what the user submitted.
I thought I just could do this, but maybe it's too much effort where there is a better solution (that I couldn't find):
const line = "Full name: John Doe" // text input
const sourcePattern = /Full name: (.*) (.*)/ // user input
let queryString = 'name=$1&lname=$2' // user input
const matches = line.match(sourcePattern)
matches.splice(0, 1)
for (let i = 0; i < matches.length; i++) {
queryString = queryString.replace(`\$${i+1}`, matches[i])
}
Any ideas?
Regular expressions are fine to extract the values from the first string. But for working with query strings there's a built in class that helps:
const entries = [...new URLSearchParams(queryString).entries()]
if (matches.length !== entries.length) {
// handle error
}
const replaced = entries.reduce((params, [key], index) => {
params.append(key, matches[index]);
return params;
}, new URLSearchParams());
You can call toString() on it to get the modified query string. Generally speaking you want to avoid doing string processing any time there's a readily available richer data type.
You could compact the code a little as follows:
const line = "Full name: John Doe" // text input
const sourcePattern = /Full name: (.*) (.*)/ // user input
let queryString = 'name=$1&lname=$2' // user input
const [_, ...matches] = line.match(sourcePattern)
console.log(queryString.split(/\$\d+/)
.map((p,i)=>`${p}${matches[i]??''}`).join(''))
Related
I have string with slash separated contains function names.
e.g.
my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName()
Within above string I want only function name i.e. getCustomer(), getControllerName() & getCsvFileName()
I searched some regex like:
let res = myString.match(/(?<=(function\s))(\w+)/g);
but its returning result as null.
Update:
Now I want to get function names without parentheses () i.e. getCustomer, getControllerName & getCsvFileName
Please help me in this
const str = "my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName()"
let tokens = [];
for (element of str.split("/"))
if (element.endsWith("()"))
tokens.push(element.split("=")[1] ?? element.split("=")[0])
console.log(tokens);
General idea: split the string along slashes, and for each of these tokens, if the token ends with () (as per Nick's suggestion), split the token along =. Append the second index of the token split along = if it exists, otherwise append the first.
A "smaller" version (using purely array methods) could be:
const str = "my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName()"
let tokens = str.split("/")
.filter(element => element.endsWith("()"))
.map(element => element.split("=")[1] ?? element.split("=")[0]);
console.log(tokens);
You can split the string that has parentheses () first like /.*?\([^)]*\)/g.
This will give array of results, and after that you can iterate the array data and for each item, you can split the = and / before function name with the help of item.split(/=|\//).
Then push the filtered function name into empty array functionNames.
Working Example:
const string = `my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName()`;
const functionNames = [];
string.match(/.*?\([^)]*\)/g).forEach(item => {
const splitString = item.split(/=|\//);
const functionName = splitString[splitString.length - 1];
functionNames.push(functionName);
});
console.log(functionNames);
As per, MDN docs the match() method returns null if it does not find a match for the provided regex in the provided search string.
The regular expression which you have provided,/(?<=(function\s))(\w+)/g matches any word that has 'function ' before it. (NOTE: a space after the word function)
Your search string my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName() does not include 'function ' before any characters. That is why you got null as result of match() method.
let yourString = 'my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName()';
let myReferenceString = 'SAMPLETEXTfunction sayHi()/function sayHello()';
let res = yourString.match(/(?<=(function\s))(\w+)/g);
let res2 = myReferenceString.match(/(?<=(function\s))(\w+)/g);
console.log("Result of your string", res);
console.log("Result of my string", res2);
My solution here,
let myreferenceString = 'my_doc/desktop/customer=getCustomer()/getCsvFileName()/controller=getControllerName()'
let res = myreferenceString.match(/((?<==)(\w+\(\)))|((?<=\/)(\w+\(\)))/g);
console.log("Result", res);
NOTE: I have used the 'Positive Look Behind regex operator', This is not supported in browsers like Safari and IE. Please do reasearch about this before considering this approach.
This is the opposite problem to Efficient JavaScript String Replacement. That solution covers the insertion of data into placeholders whilst this question covers the matching of strings and the extraction of data from placeholders.
I have a problem in understanding how to do the following in ES6 JavaScript.
I'm trying to figure out a way to match strings with placeholders and extract the contents of the placeholders as properties of an object. Perhaps an example will help.
Given the pattern:
my name is {name} and I live in {country}
It would match the string:
my name is Mark and I live in England
And provide an object:
{
name: "Mark",
country: "England"
}
The aim is to take a string and check against a number of patterns until I get a match and then have access to the placeholder values.
Can anyone point me in the right direction...
You can use named capture groups for that problem e.g.
const string = "my name is Mark and I live in England";
const regEx = /name is\s(?<name>\w+?)\b.*?live in (?<country>\w+?)\b/i;
const match = regEx.exec(string);
console.log(match?.groups);
I would be surprised if it can be done with a regex.
The way I would think about it is as follows:
Split the template by { or }
iterate over the latter template parts (every other one starting with index 1)
In each iteration, get the key, its prefix, and postfix (or next prefix)
We can then compute the start and end indices to extract the value from the string with the help of the above.
const extract = (template, str) => {
const templateParts = template.split(/{|}/);
const extracted = {};
for (let index = 1; index < templateParts.length; index += 2) {
const
possibleKey = templateParts[index],
keyPrefix = templateParts[index - 1],
nextPrefix = templateParts[index + 1];
const substringStartIndex = str.indexOf(keyPrefix) + keyPrefix.length;
const substringEndIndex = nextPrefix ? str.indexOf(nextPrefix) : str.length;
extracted[possibleKey] = str.substring(substringStartIndex, substringEndIndex);
}
return extracted;
}
console.log( extract('my name is {name} and I live in {country}', 'my name is Mark and I live in England') );
I have regex string /^(?:\[)(.*)(?:\|)(.*)(?:\|)(.*)(?:\|)(.*)(?:\|)(.*)(?:\])$/ that captures the following value [john|doe|doe#email.com|doe_avatar|manager].
I also like to capture the value with [john|doe|doe#email.com|doe_avatar] using the same regex for both. How can I do that in Javascript?
Yes, this is doable with a single regex, by enclosing the last segment and its accompanying pipe \| in an additional, optional, non-capturing group ((?:……)?).
const regex =
/^(?:\[)(.*?)(?:\|)(.*?)(?:\|)(.*?)(?:\|)(.*?)(?:(?:\|)(.*?))?(?:\])$/
const rows = [
'[john|doe|doe#email.com|doe_avatar|manager]',
'[jane|doe|jane#email.com|jane_avatar]',
]
const parse = str => {
const m = str.match(regex)
if (!m) return null
const [fullMatch, forename, surname, email, avatar, role] = m
return { fullMatch, forename, surname, email, avatar, role }
}
console.log(rows.map(parse))
As #CertainPerformance mentions below, the results from the final capturing group will be undefined if the match isn't present
If you want each section to be in a separate group, it's not possible in a single iteration of a regex pattern in JS (though it's possible in .NET and other flavors where repeated group matches can be extracted). The best you'll be able to manage is matching [ eventually followed by ], and then splitting by |s afterwards:
const extract = (str) => {
const insideBrackets = str.match(/\[([^\]]+)\]/)[1];
const sections = insideBrackets.split('|');
console.log(sections);
};
extract('[john|doe|doe#email.com|doe_avatar|manager]');
extract('[john|doe|doe#email.com|doe_avatar]');
I need to parse an email template for custom variables that occur between pairs of dollar signs, e.g:
$foo$bar$baz$foo$bar$baz$wtf
So I would want to start by extracting 'foo' above, since it comes between the first pair (1st and 2nd) of dollar signs. And then skip 'bar' but extract 'baz' as it comes between the next pair (3rd and 4th) of dollar signs.
I was able to accomplish this with split and filter as below, but am wondering, if there's a way to accomplish the same with a regular expression instead? I presume some sort of formal parser, recursive or otherwise, could be used, but that would seem like overkill in my opinion
const body = "$foo$bar$baz$foo$bar$baz$wtf";
let delimitedSegments = body.split('$');
if (delimitedSegments.length % 2 === 0) {
// discard last segment when length is even since it won't be followed by the delimiter
delimitedSegments.pop();
}
const alternatingDelimitedValues = delimitedSegments.filter((segment, index) => {
return index % 2;
});
console.log(alternatingDelimitedValues);
OUTPUT: [ 'foo', 'baz', 'bar' ]
Code also at: https://repl.it/#dexygen/findTextBetweenDollarSignDelimiterPairs
Just match the delimiter twice in the regexp
const body = "$foo$bar$baz$foo$bar$baz$wtf";
const result = body.match(/\$[^$]*\$/g).map(s => s.replace(/\$/g, ''));
console.log(result);
You could use this regex /\$\w+\$/g to get the expected output'
let regex = /\$\w+\$/g;
let str = '$foo$bar$baz$foo$bar$baz$wtf';
let result = str.match(regex).map( item => item.replace(/\$/g, ''));
console.log(result);
You can use capturing group in the regex.
const str1 = '$foo$bar$baz$foo$bar$baz$wtf';
const regex1 = /\$(\w+)\$/g;
const str2 = '*foo*bar*baz*foo*bar*baz*wtf';
const regex2 = /\*(\w+)\*/g;
const find = (str, regex) =>
new Array(str.match(regex).length)
.fill(null)
.map(m => regex.exec(str)[1]);
console.log('delimiters($)', JSON.stringify(find(str1, regex1)));
console.log('delimiters(*)', JSON.stringify(find(str2, regex2)));
I receive a string from a server and this string contains text and links (mainly starting with http://, https:// and www., very rarely different but if they are different they don't matter).
Example:
"simple text simple text simple text domain.ext/subdir again text text text youbank.com/transfertomealltheirmoney/witharegex text text text and again text"
I need a JS function that does the following:
- finds all the links (no matter if there are duplicates);
- returns an array of objects, each representing a link, together with keys that return where the link starts in the text and where it ends, something like:
[{link:"http://www.dom.ext/dir",startsAt:25,endsAt:47},
{link:"https://www.dom2.ext/dir/subdir",startsAt:57,endsAt:88},
{link:"www.dom.ext/dir",startsAt:176,endsAt:192}]
Is this possible? How?
EDIT: #Touffy: I tried this but I could not get how long is any string, only the starting index. Moreover, this does not detect www: var str = string with many links (SO does not let me post them)"
var regex =/(\b(https?|ftp|file|www):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig; var result, indices = [];
while ( (result = regex.exec(str)) ) {
indices.push({startsAt:result.index});
}; console.log(indices[0].link);console.log(indices[1].link);
One way to approach this would be with the use of regular expressions. Assuming whatever input, you can do something like
var expression = /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/gi;
var matches = input.match(expression);
Then, you can iterate through the matches to discover there starting and ending points with the use of indexOf
for(match in matches)
{
var result = {};
result['link'] = matches[match];
result['startsAt'] = input.indexOf(matches[match]);
result['endsAt'] =
input.indexOf(matches[match]) + matches[match].length;
}
Of course, you may have to tinker with the regular expression itself to suit your specific needs.
You can see the results logged by console in this fiddle
const getLinksPool = (links) => {
//you can replace the https with any links like http or www
const linksplit = links.replace(/https:/g, " https:");
let linksarray = linksplit.split(" ");
let linkspools = linksarray.filter((array) => {
return array !== "";
});
return linkspools;
};