Find Replace with RegEx failing for string ending in ? Google script - regex

I have a script in Google sheets
I am trying to find and replace headers on a sheet from a table of values on a different sheet
It is mostly working as desired but the replace is not working for any string that ends in ?
I do not know in advance when a ? will be present
I am using this:
const regex = new RegExp("(?<![^|])(?:" + search_for.join("|") + ")(?![^|])", "g");
I have tried to figure out how to correct my Regex but not getting it
Thanks in advance for your assistance with this
I have in a sheet:
search_for
replace_with
ABC Joe
MNQ
XYZ car
NNN XXX
DDD foo?
Bob bar
I have for Headers on a different sheet:
Label
Id
ABC Joe
XYZ car
DDD foo?
after running the replacement I want for headers:
Label
Id
MNQ
NNN XXX
Bob bar
what I get is:
Label
Id
MNQ
NNN XXX
DDD foo?
var data = range.getValues();
search_for.forEach(function(item, i) {
pair[item] = replace_with[i];
});
const regex = new RegExp("(?<![^|])(?:" + search_for.join("|") + ")(?![^|])", "g");
//Update Header row
//replace(/^\s+|\s+$|\s+(?=\s)/g, "") - Remove all multiple white-spaces and replaces with a single WS & trim
for(var m = 0; m<= data[0].length - 1; m++){
data[0][m] = data[0][m].replace(/^\s+|\s+$|\s+(?=\s)/g, "").replace(regex,(m) => pair[m])
}

A word of warning: what you're doing is scaring me a bit. I hope you know this is a brittle approach and it can go wrong.
You're not quoting the dynamic parts of the regex. The ? is a special character in regular expressions. I've written a solution to your problem below. Don't rely on my solution in production.
//var data = range.getValues();
var data = [
['Label', 'Id', 'ABC Joe', 'XYZ car', 'DDD foo?']
];
var search_for = [
'ABC Joe',
'XYZ car',
'DDD foo?'
];
var replace_with = [
'MNQ',
'NNN XXX',
'Bob bar'
];
var pair = {};
search_for.forEach(function(item, i) {
pair[item] = replace_with[i];
});
const regex = new RegExp("(?<![^|])(?:" + search_for.map((it) => quote(it)).join("|") + ")(?![^|])", "g");
for (var m = 0; m <= data[0].length - 1; m++) {
data[0][m] = data[0][m]
.replace(/^\s+|\s+$|\s+(?=\s)/g, "")
.replace(regex, (m) => pair[m]);
}
// see https://stackoverflow.com/a/3614500/11451
function quote(s) {
var regexpSpecialChars = /([\[\]\^\$\|\(\)\\\+\*\?\{\}\=\!])/gi;
return s.replace(regexpSpecialChars, '\\$1');
}

Can you not do something really simple like escaping all non-alphanumeric characters which would work with the example data you gave above and this seems trustworthy
function quote(s) {
var regexpSpecialChars = /((?=\W))/gi;
return s.replace(regexpSpecialChars, '\\');
}

Related

Jest cell name won't recognise regex [duplicate]

I want to add a (variable) tag to values with regex, the pattern works fine with PHP but I have troubles implementing it into JavaScript.
The pattern is (value is the variable):
/(?!(?:[^<]+>|[^>]+<\/a>))\b(value)\b/is
I escaped the backslashes:
var str = $("#div").html();
var regex = "/(?!(?:[^<]+>|[^>]+<\\/a>))\\b(" + value + ")\\b/is";
$("#div").html(str.replace(regex, "" + value + ""));
But this seem not to be right, I logged the pattern and its exactly what it should be.
Any ideas?
To create the regex from a string, you have to use JavaScript's RegExp object.
If you also want to match/replace more than one time, then you must add the g (global match) flag. Here's an example:
var stringToGoIntoTheRegex = "abc";
var regex = new RegExp("#" + stringToGoIntoTheRegex + "#", "g");
// at this point, the line above is the same as: var regex = /#abc#/g;
var input = "Hello this is #abc# some #abc# stuff.";
var output = input.replace(regex, "!!");
alert(output); // Hello this is !! some !! stuff.
JSFiddle demo here.
In the general case, escape the string before using as regex:
Not every string is a valid regex, though: there are some speciall characters, like ( or [. To work around this issue, simply escape the string before turning it into a regex. A utility function for that goes in the sample below:
function escapeRegExp(stringToGoIntoTheRegex) {
return stringToGoIntoTheRegex.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
var stringToGoIntoTheRegex = escapeRegExp("abc"); // this is the only change from above
var regex = new RegExp("#" + stringToGoIntoTheRegex + "#", "g");
// at this point, the line above is the same as: var regex = /#abc#/g;
var input = "Hello this is #abc# some #abc# stuff.";
var output = input.replace(regex, "!!");
alert(output); // Hello this is !! some !! stuff.
JSFiddle demo here.
Note: the regex in the question uses the s modifier, which didn't exist at the time of the question, but does exist -- a s (dotall) flag/modifier in JavaScript -- today.
If you are trying to use a variable value in the expression, you must use the RegExp "constructor".
var regex = "(?!(?:[^<]+>|[^>]+<\/a>))\b(" + value + ")\b";
new RegExp(regex, "is")
I found I had to double slash the \b to get it working. For example to remove "1x" words from a string using a variable, I needed to use:
str = "1x";
var regex = new RegExp("\\b"+str+"\\b","g"); // same as inv.replace(/\b1x\b/g, "")
inv=inv.replace(regex, "");
You don't need the " to define a regular expression so just:
var regex = /(?!(?:[^<]+>|[^>]+<\/a>))\b(value)\b/is; // this is valid syntax
If value is a variable and you want a dynamic regular expression then you can't use this notation; use the alternative notation.
String.replace also accepts strings as input, so you can do "fox".replace("fox", "bear");
Alternative:
var regex = new RegExp("/(?!(?:[^<]+>|[^>]+<\/a>))\b(value)\b/", "is");
var regex = new RegExp("/(?!(?:[^<]+>|[^>]+<\/a>))\b(" + value + ")\b/", "is");
var regex = new RegExp("/(?!(?:[^<]+>|[^>]+<\/a>))\b(.*?)\b/", "is");
Keep in mind that if value contains regular expressions characters like (, [ and ? you will need to escape them.
I found this thread useful - so I thought I would add the answer to my own problem.
I wanted to edit a database configuration file (datastax cassandra) from a node application in javascript and for one of the settings in the file I needed to match on a string and then replace the line following it.
This was my solution.
dse_cassandra_yaml='/etc/dse/cassandra/cassandra.yaml'
// a) find the searchString and grab all text on the following line to it
// b) replace all next line text with a newString supplied to function
// note - leaves searchString text untouched
function replaceStringNextLine(file, searchString, newString) {
fs.readFile(file, 'utf-8', function(err, data){
if (err) throw err;
// need to use double escape '\\' when putting regex in strings !
var re = "\\s+(\\-\\s(.*)?)(?:\\s|$)";
var myRegExp = new RegExp(searchString + re, "g");
var match = myRegExp.exec(data);
var replaceThis = match[1];
var writeString = data.replace(replaceThis, newString);
fs.writeFile(file, writeString, 'utf-8', function (err) {
if (err) throw err;
console.log(file + ' updated');
});
});
}
searchString = "data_file_directories:"
newString = "- /mnt/cassandra/data"
replaceStringNextLine(dse_cassandra_yaml, searchString, newString );
After running, it will change the existing data directory setting to the new one:
config file before:
data_file_directories:
- /var/lib/cassandra/data
config file after:
data_file_directories:
- /mnt/cassandra/data
Much easier way: use template literals.
var variable = 'foo'
var expression = `.*${variable}.*`
var re = new RegExp(expression, 'g')
re.test('fdjklsffoodjkslfd') // true
re.test('fdjklsfdjkslfd') // false
Using string variable(s) content as part of a more complex composed regex expression (es6|ts)
This example will replace all urls using my-domain.com to my-other-domain (both are variables).
You can do dynamic regexs by combining string values and other regex expressions within a raw string template. Using String.raw will prevent javascript from escaping any character within your string values.
// Strings with some data
const domainStr = 'my-domain.com'
const newDomain = 'my-other-domain.com'
// Make sure your string is regex friendly
// This will replace dots for '\'.
const regexUrl = /\./gm;
const substr = `\\\.`;
const domain = domainStr.replace(regexUrl, substr);
// domain is a regex friendly string: 'my-domain\.com'
console.log('Regex expresion for domain', domain)
// HERE!!! You can 'assemble a complex regex using string pieces.
const re = new RegExp( String.raw `([\'|\"]https:\/\/)(${domain})(\S+[\'|\"])`, 'gm');
// now I'll use the regex expression groups to replace the domain
const domainSubst = `$1${newDomain}$3`;
// const page contains all the html text
const result = page.replace(re, domainSubst);
note: Don't forget to use regex101.com to create, test and export REGEX code.
var string = "Hi welcome to stack overflow"
var toSearch = "stack"
//case insensitive search
var result = string.search(new RegExp(toSearch, "i")) > 0 ? 'Matched' : 'notMatched'
https://jsfiddle.net/9f0mb6Lz/
Hope this helps

Split the data using regex with google script

As a newbie, I have tried a lot to solve the below problem.
My Current table
TestID TestName Name Url
1592461 Google-page (www.google.com)
1592467 Yahoo - Page (www.yahoo.com)
I am trying to split the data present in the column "TestName" and add the result to the columns "Name" and "URL" as given in the below table
Expected table
TestID TestName Name Url
1592461 Google-page (www.google.com) Google-page www.google.com
1592467 Yahoo - Page (www.yahoo.com) Yahoo - Page www.yahoo.com
I have tried to compile the following script but was unsuccessful.
function getUrl(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Sheet1");
var s2 = ss.getSheetByName("Sheet2");
var data = s1.getSheetValues(1, 2, s1.getLastRow() , 1);
var regExp = new RegExp("\(([^]]+)\)");
var row = [];
for(i = 0; i<data; i++) {
var url = regExp.exec(data)[i];
var output = s2.getRange("C2").setValue(url);
logger.log(url);
return url;
}
}
Could someone please help me in solving this.
In addition, I just wanted to let you know this can also be done with a (rather simple) formula. Enter in C1
=ArrayFormula(split(substitute(B2:B3, ")",""), "("))
Change range to suit.
I have an impression you want to get the data from Column 2 of the current spreadsheet into Column 3 and 4 in the same spreadsheet.
I suggest using the following regex:
var regExp = /(.*?)\(([^)]+)\)/;
The (.*?) will capture any 0+ chars other than line break chars into Group 1 (all before () then \( will match a ( and then ([^)]+) will capture 1+ chars other than ) into Group 2 (the URL) and then the \) will match a ).
And use it to analyze Column B data:
function getUrl(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Sheet1");
var src_range = s1.getRange("B:B"); // Grab the Column B range
var regExp = /(.*?)\(([^)]+)\)/; // Define the regex
for(i = 1; i<=src_range.getLastRow(); i++) { // Loop through all the cells in the range
if (!src_range.getCell(i, 1).isBlank()) { // If the cell is not blank, process it
var m = regExp.exec(src_range.getCell(i, 1).getValue()); // Run the regex
if (m) { // If there is a match
var text = m[1]; // Text to be placed into Column C
s1.getRange('C' + i).setValue(text);
var url = m[2]; // URL to be placed into Column D
s1.getRange('D' + i).setValue(url);
}
}
}
}
See a sample document.

regex equals to something exactly but does not equal to something else

my regex query below does an exact match of a word say Bob or Bill for example
var regExp = new RegExp("^" + inputVal + "$", 'i');
what i want it to do is match anything exactly (Bob or Bill Etc) but not match Fred
so match anything exactly except for Fred, does that make sense?
anyone help me out as to how i do that?
Thanks
EDIT 2:
i thought id show my actual script instead, what im doing is searching a table, and im page load i want to hide rows that contain a string. so if exlucde lenght is greater than 0 hide that row...
function searchPagingTable(inputVal, tablename, fixedsearch, exclude) {
var table = $(tablename);
table.find('tr:not(.header)').each(function (index, row) {
var allCells = $(row).find('td');
if (allCells.length > 0) {
var found = false;
allCells.each(function (index, td) {
if (fixedsearch == 1) {
var regExp = new RegExp("^" + inputVal + "$", 'i');
}
else if (exclude.length > 0)
{
var regExp = new RegExp("^(?!" + exclude + ")", "i");
}
else {
var regExp = new RegExp(inputVal, 'i');
}
if (regExp.test($(td).text())) {
found = true;
return false;
}
});
if (found == true) $(row).show().removeClass('exclude'); else $(row).hide().addClass('exclude');
}
});
pa
ginate();
}
That would be
var exclude = "Fred"
var regExp = new RegExp("^(?!.*" + exclude + ")", "i");
This regex matches any string except those that contain Fred. It doesn't actually match any characters in the string, but that's sufficient if you're just looking for a true/false result.
This will also find strings that contain Alfred or Fredo, so if you don't want that, you need to tell the regex only to look for entire words using word boundaries:
var regExp = new RegExp("^(?!.*\\b" + exclude + "\\b)", "i");
You need to make sure that your exclude string only contains ASCII letters/digits (or underscores) for this to work correctly.
You could populate a list of names you wish to match against:
var validNames = ['bob', 'bill'];
Then lowercase each input and match against the list:
if (validNames.indexOf(inputVal.toLowerCase()) != -1) {
// it's a good name
}
For older browsers you have to shim Array.indexOf()
var re = new RegExp('^\\s*Fred\\s*$','i');
if (inputVal.match(re)) {
// Fred has been found
} else {
// Anything has been found
}

Convert REST content to comma seperated string

I'm having some content in a REST API that I would like to convert into a simple comma-separated string - it looks as so:
{
"2299": 2299,
"2224": 2224,
"2135": 2135,
"2196": 2196,
"2200": 2200
}
and this is how I do that:
foreach (var product in titles)
{
TextBox1.Text = product["children"].ToString() + "<br/>";
}
This is ho I would like my output to look like:
2299,2224,2135,2196,2200
I've tried some various REGEX with no further luck - I'm using c# by the way...
Any idears...?
Solved...!
By researching a few hours more on google I found the solution:
string c = product["children"].ToString();
foreach (Match m in Regex.Matches(c, "(?<=\")[\\w]+(?!=\")"))
{
string children = m.Value + ",";
}
This schript does the job for me... :)
Using JSON.NET, you could simply do
var dict = JToken.Parse(textFromYourQuestion).ToObject<Dictionary<string, int>>().Values;
var stringYouWant = string.Join(",", dict.Values);

Replace each RegExp match with different text in ActionScript 3

I'd like to know how to replace each match with a different text?
Let's say the source text is:
var strSource:String = "find it and replace what you find.";
..and we have a regex such as:
var re:RegExp = /\bfind\b/g;
Now, I need to replace each match with different text (for example):
var replacement:String = "replacement_" + increment.toString();
So the output would be something like:
output = "replacement_1 it and replace what you replacement_2";
Any help is appreciated..
You could also use a replacement function, something like this:
var increment : int = -1; // start at -1 so the first replacement will be 0
strSource.replace( /(\b_)(.*?_ID\b)/gim , function() {
return arguments[1] + "replacement_" + (increment++).toString();
} );
I came up with a solution finally..
Here it is, if anyone needs:
var re:RegExp = /(\b_)(.*?_ID\b)/gim;
var increment:int = 0;
var output:Object = re.exec(strSource);
while (output != null)
{
var replacement:String = output[1] + "replacement_" + increment.toString();
strSource = strSource.substring(0, output.index) + replacement + strSource.substring(re.lastIndex, strSource.length);
output = re.exec(strSource);
increment++;
}
Thanks anyway...
leave off the g (global) flag and repeat the search with the appropriate replace string. Loop until the search fails
Not sure about actionscript, but in many other regex implementations you can usually pass a callback function that will execute logic for each match and replace.