Regexp search not surrounded by - javascript

I want to find all occurences of % that are not within quotation characters.
Example> "test% testing % '% hello' " would return ["%","%"]
Looking at another stack overflow thread this is what I found:
var patt = /!["'],*%,*!['"]/g
var str = "testing 123 '%' % '% ' "
var res = str.match(patt);
However this gives me null. Have you any tips of what I should do?
Demo

You could try the below positive lookahead assertion based regex.
> var s = "test% testing % '% hello' "
> s.match(/%(?=(?:[^']*'[^']*')*[^']*$)/g)
[ '%', '%' ]
> var str = "testing %"
undefined
> str.match(/%(?=(?:[^']*'[^']*')*[^']*$)/g)
[ '%' ]
> var str1 = "testing '%'"
undefined
> str1.match(/%(?=(?:[^']*'[^']*')*[^']*$)/g)
null

Try this:
var patt= /[^"'].*?(%).*?[^'"]/g ;
var str = "testing 123 '%' % '% ' "
var res = str.match(patt);
console.dir(res[1]); // result will be in the 1st match group: res[1]
Here is the link to the online testing.
Explanation:
[^"'] - any character except " or '
.*? any characters (except new line) any times or zero times not greedy.
Update
Actually you must check if behing and ahead of % there are no quotes.
But:
JavaScript regular expressions do not support lookbehinds
So you have no way to identify " or ' preceding % sign unless more restrictions are applied.
I'd suggest to do searching in php or other language (where lookbehind is supported) or impose more conditions.

Since I'm not a big fan of regular expressions, here's my approach.
What is important in my answer, if there would be a trailing quote in the string, the other answers won't work. In other words, only my answer works in cases where there is odd number of quotes.
function countUnquoted(str, charToCount) {
var i = 0,
len = str.length,
count = 0,
suspects = 0,
char,
flag = false;
for (; i < len; i++) {
char = str.substr(i, 1);
if ("'" === char) {
flag = !flag;
suspects = 0;
} else if (charToCount === char && !flag) {
count++;
} else if (charToCount === char) {
suspects++;
}
}
//this way we can also count occurences in such situation
//that the quotation mark has been opened but not closed till the end of string
if (flag) {
count += suspects;
}
return count;
}
As far as I believe, you wanted to count those percent signs, so there's no need to put them in an array.
In case you really, really need to fill this array, you can do it like that:
function matchUnquoted(str, charToMatch) {
var res = [],
i = 0,
count = countUnquoted(str, charToMatch);
for (; i < count; i++) {
res.push('%');
}
return res;
}
matchUnquoted("test% testing % '% hello' ", "%");
Trailing quote
Here's a comparison of a case when there is a trailing ' (not closed) in the string.
> var s = "test% testing % '% hello' asd ' asd %"
> matchUnquoted(s, '%')
['%', '%', '%']
>
> // Avinash Raj's answer
> s.match(/%(?=(?:[^']*'[^']*')*[^']*$)/g)
['%', '%']

Use this regex: (['"]).*?\1|(%) and the second capture group will have all the % signs that are not inside single or double quotes.
Breakdown:
(['"]).*?\1 captures a single or double quote, followed by anything (lazy) up to a matching single or double quote
|(%) captures a % only if it wasn't slurped up by the first part of the alternation (i.e., if it's not in quotes)

Related

Finding the index to a non-specified character

Let's say for example I have a string
thisIsThisTuesday Day
I want to find the index of all the capital letters, test if there is a space before it, and if not insert one. I would need the index of each one.
At least from what I can see indexOf(String) will only produce the index of the first occurance of the character T/t
This :
for(i=0;i<str.length;i++){
let char=str[i];
if(isNaN(char*1)&&char==char.toUpperCase()){
y=str.indexOf(char);
console.log(char,y)
}
}
would produce the capital letters, and their indexes but will only display the first occurrence of the character in question. I feel pretty confident that the part I am missing is a for() loop in order to move the index iteration..but it escapes me.
Thank you in advance!
You can use a regex:
It matches any non-whitespace character followed by a capital letter and replaces it by the two characters with a space between.
const str = "thisIsThisTuesday Day";
const newstr = str.replace(/([^ ])([A-Z])/g, "$1 $2");
console.log(newstr);
You can use the following regular expression:
/(?<=\S)(?=[A-Z])/g
The replace will insert spaced between characters which are non-space followed by a capital letter.
See example below:
let str = "thisIsThisTuesday Day";
const res = str.replace(/(?<=\S)(?=[A-Z])/g, ' ');
console.log(res);
Note: As pointed out ?<= (positive lookbehind) is currently not be available in all browsers.
Actually, the String.indexOf function can take a second argument, specifying the character it should start searching from. Take a look at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
But, if you just want to find all capital letters and prefix them with a space character, if one is not found, there are many approaches, for example:
var str = "thisIsThisTuesday Day";
var ret = '';
for (var i=0; i<str.length; i++) {
if (str.substr(i, 1) == str.substr(i, 1).toUpperCase()) {
if ((i > 0) && (str.substr(i - 1,1) != " "))
ret += " ";
}
ret += str.substr(i,1);
}
After running this, ret will hold the value "this Is This Tuesday Day"
You could iterate over the string and check if each character is a capital. Something like this:
const s = 'thisIsThisTuesday Day';
const format = (s) => {
let string = '';
for (let c of s) {
if (c.match(/[A-Z]/)) string += ' ';
string += c;
}
return string;
};
console.log(format(s));
Or alternatively with reduce function:
const s = 'thisIsThisTuesday Day';
const format = (s) => s.split('').reduce((acc, c) => c.match(/[A-Z]/) ? acc + ` ${c}` : acc + c, '');
console.log(format(s));

Adding a whitespace in front of the first number in a string

I need a whitespace to be added in front of the first number of a string, unless there is one already and unless the number is the first character in the string.
So I wrote this JS code:
document.getElementById('billing:street1').addEventListener('blur', function() {
var value = document.getElementById('billing:street1').value;
var array = value.match(/\d{1,}/g);
if (array !== null) {
var number = array[0];
var index = value.indexOf(number);
if(index !== 0){
var street = value.substring(0, index);
var housenumber = value.substring(index);
if (street[street.length - 1] !== ' ') {
document.getElementById('billing:street1').value = street + ' ' + housenumber;
}
}
}
});
Fiddle
It works fine, but I feel like this can probably be done in a smarter, more compact way.
Also, JQuery suggestions welcome, I am just not very familiar with it.
Try this one :
const addSpace = (str) => {
return str.replace(/(\D)(\d)/, "$1 $2")
}
console.log(addSpace("12345")) // "12345"
console.log(addSpace("city12345")) // "city 12345"
console.log(addSpace("city")) // "city"
(\D) captures a non-digit
(\d) captures a digit
so (\D)(\d) means : non-digit followed by a digit
that we replace with "$1 $2" = captured1 + space + captured2
You can do it by using only regular expressions. For example:
var s = "abcde45";
if(!s.match(/\s\d/)){
s = s.replace(/(\d)/, ' $1');
}
console.log(s); // "abcde 45"
UPD : Of course, if you have string with a wrong syntax(e.g no numbers inside), that code wouldn't work.

adding a space to every space in a string, then cycling back around until length is met

I have the following while loop as part of my text justify function. The idea is that I have text strings (str) that need to be justified (spaces added to existing spaces in between words) to equal to a given length (len)
The catch is I can only add one space to an existing space at a time before I iterate over to the next space in the string and add another space there. If that's it for all spaces in the string and it's still not at the required length, I cycle back over to the original space (now two spaces) and add another. Then it goes to the next space between words and so on and so on. The idea is that any spaces between words in the string should not have a differential of more than one space (i.e. Lorem---ipsum--dolor--sit, not Lorem----ipsum--dolor-sit)
From my research, I decided that using a substring method off the original string to add that first extra space, then I will increment the index and move to the next space in the string and repeat the add. Here's my code:
var indexOf = str.indexOf(" ", 0);
if ( indexOf > -1 ) {
while ( indexOf > -1 && str.length < len ) {
//using a regexp to find a space before a character
var space = /\s(?=\b)/.exec(str);
str = str.substring(0, indexOf + 1) + " " + str.substring(indexOf + 1);
//go to next space in string
indexOf = str.indexOf(space, indexOf + 2);
if ( indexOf === -1 ) {
//loops back to beginning of string
indexOf = str.indexOf(space, 0);
}
}
}
finalResults.push(str);
This code works most of the time, but I noticed that there are instances where the cycle of spacing is not correct. For example, it generates the following string:
sit----amet,--blandit
when the correct iteration would be
sit---amet,---blandit
Any assistance in making this code properly iterate over every space (to add one space) in the string once, then cycling back around to the beginning of the string to start over until the desired length is achieved would be most appreciated.
I think it's more efficient to compute the number spaces required in the beginning.
var s = "today is a friday";
var totalLength = 40;
var tokens = s.split(/\s+/);
var noSpaceLength = s.replace(/\s+/g,'').length;
var minSpace = Math.floor((totalLength - noSpaceLength)/(tokens.length-1));
var remainder = (totalLength - noSpaceLength) % (tokens.length-1);
var out = tokens[0];
for (var i = 1; i < tokens.length; i++) {
var spaces = (i <= remainder ? minSpace+1 : minSpace);
out += "-".repeat(spaces) + tokens[i];
}
$('#out').text(out);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="out"></div>
This solution
splits the string (s) into words in an array (a)
finds the number of spaces to be added between all words (add)
finds the remainder of spaces to be added between first words (rem)
then sticks the words with add spaces + one if rem is not exhausted
Code
var s = "Caballo sin Nombre"; // assume one space between words
var len = 21; // desired length
var need = len - s.length;
var a = s.split(/ /); // split s
// need>0 and at least two words
if (need > 0 && a.length>1) {
var add = Math.floor(need / (a.length-1)) + 1; // all spaces need that (+existing)
var rem = need % (a.length-1); // remainder
var sp = '';
while (add-- > 0) sp += ' ';
// replace
var i,res = ''; // result
for (i=0 ; i<a.length-1 ; i++) {
res += a[i] + sp;
if (rem-- > 0) res += ' '; // remainder
}
res += a[i];
s = res;
}
console.log("'" + s + "' is " + s.length + " chars long.");
This function adds the spaces using a global replace, carefully limiting the text size.
function expand (txt, colwidth) {
txt = txt.replace (/\s\s+/, ' '); // Ensure no multiple spaces in txt
for (var spaces = ' ', // Spaces to check for
limit = colwidth - txt.length; // number of additional spaces required
limit > 0; // do while limit is positive
spaces += ' ') // add 1 to spaces to search for
txt = txt.replace (RegExp (spaces, 'g'),
function (r) {
// If limit > 0 then add a space else do not.
return limit > 0 && --limit ? r + ' ' : r
});
return txt;
}
for (var w = 21; w--;) console.log (expand ('this is a test.', w));
Shows this on console:
this is a test.
this is a test.
this is a test.
this is a test.
14 this is a test.

Backward capture group concatenated with forward capture group

I think the title says it all. I'm trying to get groups and concatenate them together.
I have this text:
GPX 10.802.123/3843­ 1 -­ IDENTIFIER 48
And I want this output:
IDENTIFIER 10.802.123/3843-48
So I want to explicitly say, I want to capture one group before this word and after, then concatenate both, only using regex. Is this possible?
I can already extract the 48 like this:
var text = GPX 10.802.123/3843­ 1 -­ IDENTIFIER 48
var reg = new RegExp('IDENTIFIER' + '.*?(\\d\\S*)', 'i');
var match = reg.exec(text);
Output:
48
Can it be done?
I'm offering 200 points.
You must precisely define the groups that you want to extract before and after the word. If you define the group before the word as four or more non-whitespace characters, and the group after the word as one or more non-whitespace characters, you can use the following regular expression.
var re = new RegExp('(\\S{4,})\\s+(?:\\S{1,3}\\s+)*?' + word + '.*?(\\S+)', 'i');
var groups = re.exec(text);
if (groups !== null) {
var result = groups[1] + groups[2];
}
Let me break down the regular expression. Note that we have to escape the backslashes because we're writing a regular expression inside a string.
(\\S{4,}) captures a group of four or more non-whitespace characters
\\s+ matches one or more whitespace characters
(?: indicates the start of a non-capturing group
\\S{1,3} matches one to three non-whitespace characters
\\s+ matches one or more whitespace characters
)*? makes the non-capturing group match zero or more times, as few times as possible
word matches whatever was in the variable word when the regular expression was compiled
.*? matches any character zero or more times, as few times as possible
(\\S+) captures one or more non-whitespace characters
the 'i' flag makes this a case-insensitive regular expression
Observe that our use of the ? modifier allows us to capture the nearest groups before and after the word.
You can match the regular expression globally in the text by adding the g flag. The snippet below demonstrates how to extract all matches.
function forward_and_backward(word, text) {
var re = new RegExp('(\\S{4,})\\s+(?:\\S{1,3}\\s+)*?' + word + '.*?(\\S+)', 'ig');
// Find all matches and make an array of results.
var results = [];
while (true) {
var groups = re.exec(text);
if (groups === null) {
return results;
}
var result = groups[1] + groups[2];
results.push(result);
}
}
var sampleText = " GPX 10.802.123/3843- 1 -- IDENTIFIER 48 A BC 444.2345.1.1/99x 28 - - Identifier 580 X Y Z 9.22.16.1043/73+ 0 *** identifier 6800";
results = forward_and_backward('IDENTIFIER', sampleText);
for (var i = 0; i < results.length; ++i) {
document.write('result ' + i + ': "' + results[i] + '"<br><br>');
}
body {
font-family: monospace;
}
You can do:
var text = 'GPX 10.802.123/3843­ 1 -­ IDENTIFIER 48';
var match = /GPX\s+(.+?) \d .*?(IDENTIFIER).*?(\d\S*)/i.exec(text);
var output = match[2] + ' ' + match[1] + '-' + match[3];
//=> "IDENTIFIER 10.802.123/3843­-48"
This would be possible through replace function.
var s = 'GPX 10.802.123/3843­ 1 -­ IDENTIFIER 48'
s.replace(/.*?(\S+)\s+\d+\s*-\s*(IDENTIFIER)\s*(\d+).*/, "$2 $1-$3")
^\s*\S+\s*\b(\d+(?:[./]\d+)+)\b.*?-.*?\b(\S+)\b\s*(\d+)\s*$
You can try this.Replace by $2 $1-$3.See demo.
https://regex101.com/r/sS2dM8/38
var re = /^\s*\S+\s*\b(\d+(?:[.\/]\d+)+)\b.*?-.*?\b(\S+)\b\s*(\d+)\s*$/gm;
var str = 'GPX 10.802.123/3843­ 1 -­ IDENTIFIER 48';
var subst = '$2 $1-$3';
var result = str.replace(re, subst);
You can use split too:
var text = 'GPX 10.802.123/3843­ 1 -­ IDENTIFIER 48';
var parts = text.split(/\s+/);
if (parts[4] == 'IDENTIFIER') {
var result = parts[4] + ' ' + parts[1] + '-' + parts[5];
console.log(result);
}

JavaScript - How can I replace string with certain char in to a function

How can I do this using regex, replacing every string with ! wrapped in function:
Examples:
3! => fact(3)
2.321! => fact(2.321)
(3.2+1)! => fact(3.2+1)
(sqrt(2)+2/2^2)! => fact(sqrt(2)+2/2^2)
Given your examples, you don't need a regex at all:
var s = "3!"; //for example
if (s[s.length-1] === "!")
s = "fact(" + s.substr(0, s.length-1) + ")";
Not doubling the parentheses for the last case just requires another test:
var s = "(sqrt(2)+2/2^2)!"; //for example
if (s[s.length-1] === "!") {
if(s.length > 1 && s[0] === "(" && s[s.length-2] === ")")
s = "fact" + s.substr(0, s.length-1);
else
s = "fact(" + s.substr(0, s.length-1) + ")";
}
My own answer i just found out was:
Number.prototype.fact = function(n) {return fact(this,2)}
str = str.replace(/[\d|\d.\d]+/g, function(n) {return "(" + n + ")"}).replace(/\!/g, ".fact()")
But I'll see if the other answers might be much better, think they are
Here is as the op requested; using regexp:
"3*(2+1)!".replace(/([1-9\.\(\)\*\+\^\-]+)/igm,"fact($1)");
You might end up with double parentheses:
"(2+1)!".replace(/([1-9\.\(\)\*\+\^\-]+)/igm,"fact($1)");
"(sqrt(2)+2/2^2)!".replace(/(.*)!/g, "fact($1)");
Fiddle with it!
(.*)!
Match the regular expression below and capture its match into backreference number 1 (.*)
Match any single character that is not a line break character .
Between zero and unlimited times, as many times as possible, giving back as needed (greedy) *
Match the character “!” literally !
var testArr = [];
testArr.push("3!");
testArr.push("2.321!");
testArr.push("(3.2+1)!");
testArr.push("(sqrt(2)+2/2^2)!");
//Have some fun with the name. Why not?
function ohIsThatAFact(str) {
if (str.slice(-1)==="!") {
str = str.replace("!","");
if(str[0]==="(" && str.slice(-1)===")")
str = "fact"+str;
else
str = "fact("+str+")";
}
return str;
}
for (var i = 0; i < testArr.length; i++) {
var testCase = ohIsThatAFact(testArr[i]);
document.write(testCase + "<br />");
}
Fiddle example

Categories

Resources