Remove part of string using regex and replace - javascript

give a string like this (it can be any pattern, but the following format):
lastname/firstname/_/country/postalCode/_/regionId/city/addressFirst/addressSecond/_/phone
I am making a function that, when I pass some address parts, the function will return those parts and remove extras part that are not requested and mantaining maximum one spacing _ if many where removed:
FE :
exs :
input : ["country", "postalCode"]
return "country/postalCode
input : ["lastname", "firstname", "regionId"]
return "lastname/firstname/_/regionId"
input : ["firstname", "country", "regionId", "city"]
return "firstname/_/country/_/regionId/city"
input : ["country", "regionId", "phone"]
return "country/_/regionId/_/phone"
Now, My method is as follow :
type AddressPart = "firstname" | "lastname" | ... | "phone";
const allAddressParts = ["firstname", "lastname", ... ,"phone"];
static getAddress(
format = "lastname/firstname/_/country/postalCode/_/regionId/city/addressFirst/addressSecond/_/phone"
parts: AddressPart[],
) {
const toRemove = allAddressParts.filter((part) => !parts.includes(part));
toRemove.forEach((part) => {
format = format
.replace(`_/${part}/_`, '_')
.replace(new RegExp(part + '/?'), '');
});
return format;
}
the above looks ok, but it fail at the end and start :
_/country/postalCode/_/regionId/city/addressFirst/addressSecond/_/
How can I remove /_/ or _/ if it is situated on start or at the end without relooping over the array ?

You can use regexes on the final string to remove leading and trailing unwanted symbols
The first regex looks for:
Start of the string, with the ^ symbol
One or more "_/", using
parentheses to group the character pair,
the + sign to allow 1 or more, and
a backslash to escape the forward slash
And the second regex looks for:
a "/"
zero or more "_/", with the *
the end of the string with the $ symbol
s1 = "_/country/postalCode/_/regionId/city/addressFirst/addressSecond/_/"
s2 = s1.replace(/^(_\/)+/,"").replace(/\/(_\/)*$/,"")
console.log(s2)
You may be able to simplify
If you know for certain that there will no more than one "_" at the beginning and one at the end, you don't need the parentheses and +/* symbols.

Related

Javascript: GUID: RegEx: string to GUID

I have a textbox that a user can paste into using Ctrl+V. I would like to restrict the textbox to accept just GUIDs. I tried to write a small function that would format an input string to a GUID based on RegEx, but I can't seem to be able to do it. I tried following the below post:
Javascript string to Guid
function stringToGUID()
{
var strInput = 'b6b954d9cbac4b18b0d5a0f725695f1ca98d64e456f76';
var strOutput = strInput.replace(/([0-f]{8})([0-f]{4})([0-f]{4})([0-f]{4})([0-f]{12})/,"$1-$2-$3-$4-$5");
console.log(strOutput );
//from my understanding, the input string could be any sequence of 0-9 or a-f of any length and a valid giud patterened string would be the result in the above code. This doesn't seem to be the case;
//I would like to extract first 32 characters; how do I do that?
}
I suggest that you remove the dashes, truncate to 32 characters, and then test if the remaining characters are valid before inserting the dashes:
function stringToGUID()
{
var input = 'b6b954d9cbac4b18b0d5a0f725695f1ca98d64e456f76';
let g = input.replace("-", "");
g = g.substring(0, 32);
if (/^[0-9A-F]{32}$/i.test(g)) {
g = g.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, "$1-$2-$3-$4-$5");
}
console.log(g);
}
stringToGUID();
(The i at the end of the regex makes it case-insensitive.)
You are already matching 32 characters with the pattern, so there is no need to get a separate operation to get 32 characters to test against.
You can replace all the hyphens with an empty string, and then match the pattern from the start of the string using ^
Then first check if there is a match, and if there is do the replacement with the 5 groups and hyphens in between. If there is not match, return the original string.
The function stringToGUID() by itself does not do anything except log a string that is hardcoded in the function. To extend its functionality, you can pass a parameter.
function stringToGUID(s) {
const regex = /^([0-f]{8})([0-f]{4})([0-f]{4})([0-f]{4})([0-f]{12})/;
const m = s.replace(/-+/g, '').match(regex);
return m ? `${m[1]}-${m[2]}-${m[3]}-${m[4]}-${m[5]}` : s;
}
[
'b6b954d9cbac4b18b0d5a0f725695f1ca98d64e456f76',
'b6b954d9-cbac-4b18-b0d5-a0f725695f1c',
'----54d9cbac4b18b0d5a0f725695f1ca98d64e456f76',
'!##$%'
].forEach(s => {
console.log(stringToGUID(s));
});

Regex - Numeric regex with . and ,

I have to create a regex that allows the user to input only a number (using . or ,)
so these examples are both valid:
8,5
8.5
here's my current code
private regex: RegExp = new RegExp(/^\d*[\,\.]{0,1}\d{1,2}/g);
However this allows me to input 8.,5 which is obviously bad. How can I change my regex so that the user can only place 1 of the decimal characters , OR .?
EDIT:
I've tried alot of answers, but most of them don't work (I can't place any decimal characters). Basically I'm creating a directive in angular that converts <input type="text"> to an numeric input (I can't use type="number")
Here's my directive code (see Angular2 - Input Field To Accept Only Numbers)
#Directive({
selector: "[OnlyNumber]"
})
export class OnlyNumberDirective {
// Allow decimal numbers. The \. is only allowed once to occur
private regex: RegExp = new RegExp(/^(?=.+)\d*(?:[\,\.]\d{1,2})?$/g);
// Allow key codes for special events. Reflect :
// Backspace, tab, end, home
private specialKeys: Array<string> = ["Backspace", "Tab", "End", "Home"];
constructor(private el: ElementRef) {
}
#HostListener("keydown", ["$event"])
onKeyDown(event: KeyboardEvent) {
// Allow Backspace, tab, end, and home keys
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
let current: string = this.el.nativeElement.value;
let next: string = current.concat(event.key);
if (next && !String(next).match(this.regex)) {
event.preventDefault();
}
}
}
and here's how I use it in my template:
<mat-form-field class="numeric-textbox">
<input matInput
OnlyNumber
#model="ngModel"
placeholder="{{ label }}"
[ngModel]="selectedValue"/>
<mat-error><ng-content></ng-content></mat-error>
</mat-form-field>
You should specify the end of input string with $ without which a partial match will happen. You shouldn't look for \d* unless you want to match values like .5 or ,5 otherwise they will match as a valid input.
^\d+(?:[.,]\d{1,2})?$
Note: You don't need to escape dots or commas inside a character class and a quantifier like [.,]{0,1} is literally equal to [.,]?
Live demo:
document.getElementById("number").addEventListener("keyup",function(e) {
console.log(this.value.match(/^\d+(?:[.,]\d{1,2})?$/));
});
<input type="text" id="number" placeholder="Enter a number">
Update, based on comments
^(?![.,]?$)\d*[,.]?(?:\d{1,2})?$
This allows any number optionally followed or preceded by a decimal point or comma.
Live demo
The regex is correct, buy you just need to match the whole string:
^ start of the string
$ end of the string
However, the regex can be improved with:
^ : for start of string
\d+ : for at least 1 digit
(
[\,\.] : 1 comma
\d{1,2} : followed by 1 digit or two
)? : keep this optionnal. We can get numbers without commas
$ : end of string
Final regex may be:
/^\d+([\,\.]\d{1,2})?$/
Try: /^(?=.+)\d*(?:[,.]\d{1,2})?$/g
let regex = /^(?=.+)\d*(?:[,.]\d{1,2})?$/g,
strings = ["5", "50", "500", ".5", ",5","5.5","5,5", "5.55", "5,55", "5.555", "5,555", "5,,5", "5..5", "5,.5", "5.,5", "5,", "5."];
strings.forEach((string) => {
console.log(`${regex} ${string.match(regex) ? `matches ${string.match(regex)}`: `has no match for ${string}`}`);
});
This will match:
From the start, lookahead to make sure that there are characters present (one or more), and then begin matching: any amount of digits (0 or more), and the following optional: a comma or a dot, and then 1 or 2 digits before the end of the string.
Your regex is perfectly fine, you just need to specify the line's termination. You're currently matching 8 from 8,.5 which is likely why you're experiencing issues with your regex. I assume you're using JavaScript's test() function and getting true as a response for that string. Simply append $ to your regex and you'll get the correct answer. You can also simplify your regex to the following (also commented out in the snippet below). You can also probably drop the g flag as you're trying to match the string once, not multiple times:
^\d*[,.]?\d{1,2}$
What you're matching:
var a = ['8.5', '8,5', '.5', ',5', '8.,5']
var r = /^\d*[\,\.]{0,1}\d{1,2}/g
a.forEach(function(s){
console.log(s.match(r))
})
What you should be matching:
var a = ['8.5', '8,5', '.5', ',5', '8.,5']
var r = /^\d*[\,\.]{0,1}\d{1,2}$/g
// var r = /^\d*[,.]?\d{1,2}$/
a.forEach(function(s){
console.log(s.match(r))
})

JISON tokens using same characters

I have some troubles using JISON. I am trying to match 2 strings in square brackets splitted by a dot. The problem I encounter is that if any of the strings starts with a number or minus, it detects it as MINUS or NUMBER [which is right], but I want first to check if STRING regex matches the string the user inputs, and after that to check the NUMBER or MINUS.
%lex
%%
'-' return 'MINUS'
[0-9]+ return 'NUMBER'
[_-]+ return 'SYMBOL'
[a-zA-Z0-9_-\s]+ return 'STRING'
'.' return 'DOT'
'[' return '['
']' return ']'
<<EOF>> return 'EOF'
/lex
%start program
%%
program
: e EOF
{console.log(JSON.stringify($1, null, 4)); return $1; }
;
e
: NUMBER
{ $$ = {node: 'NUMBER', value: parseInt(yytext)}; }
| STRING
{ $$ = {node: 'STRING', value: yytext}; }
| SYMBOL
{ $$ = {node: 'SYMBOL', value: yytext}; }
| '[' STRING DOT STRING ']'
{ $$ = {node: 'SQUARE_BRACKETS', left: $2, right: $4}; }
;
It should match [2-20-March.Gum] (for example).
Thank you!
STRING will match any MINUS, NUMBER or SYMBOL, so if you insist that STRING be matched first, none of these will ever be matched and you might as well just remove them from the file. Jison just matches patterns in order until it finds one which matches.
Perhaps your intention is to match the contents of bracketed expressions differently; if so, you will need a different tokenising strategy (and you need to be clearer about what your requirements might be).
To be honest, I don't understand
[_-]+ return SYMBOL;
Do you really intend SYMBOL to only match sequences containing dashes and underlines? (-_____-_____-)

How to use a variable inside Regex?

I have this line in my loop:
var regex1 = new RegExp('' + myClass + '[:*].*');
var rule1 = string.match(regex1)
Where "string" is a string of class selectors, for example: .hb-border-top:before, .hb-border-left
and "myClass" is a class: .hb-border-top
As I cycle through strings, i need to match strings that have "myClass" in them, including :before and :hover but not including things like hb-border-top2.
My idea for this regex is to match hb-border-top and then :* to match none or more colons and then the rest of the string.
I need to match:
.hb-fill-top::before
.hb-fill-top:hover::before
.hb-fill-top
.hb-fill-top:hover
but the above returns only:
.hb-fill-top::before
.hb-fill-top:hover::before
.hb-fill-top:hover
and doesn't return .hb-fill-top itself.
So, it has to match .hb-fill-top itself and then anything that follows as long as it starts with :
EDIT:
Picture below: my strings are the contents of {selectorText}.
A string is either a single class or a class with a pseudo element, or a rule with few clases in it, divided by commas.
each string that contains .hb-fill-top ONLY or .hb-fill-top: + something (hover, after, etc) has to be selected. Class is gonna be in variable "myClass" hence my issue as I can't be too precise.
I understand you want to get any CSS selector name that contains the value anywhere inside and has EITHER : and 0+ chars up to the end of string OR finish right there.
Then, to get matches for the .hb-fill-top value you need a solution like
/\.hb-fill-top(?::.*)?$/
and the following JS code to make it all work:
var key = ".hb-fill-top";
var rx = RegExp(key.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(?::.*)?$");
var ss = ["something.hb-fill-top::before","something2.hb-fill-top:hover::before","something3.hb-fill-top",".hb-fill-top:hover",".hb-fill-top2:hover",".hb-fill-top-2:hover",".hb-fill-top-bg-br"];
var res = ss.filter(x => rx.test(x));
console.log(res);
Note that .replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') code is necessary to escape the . that is a special regex metacharacter that matches any char but a line break char. See Is there a RegExp.escape function in Javascript?.
The ^ matches the start of a string.
(?::.*)?$ will match:
(?::.*)?$ - an optional (due to the last ? quantifier that matches 1 or 0 occurrences of the quantified subpattern) sequence ((?:...)? is a non-capturing group) of a
: - a colon
.* - any 0+ chars other than line break chars
$ - end of the string.
var regex1 = new RegExp(`^\\${myClass}(:{1,2}\\w+)*$`)
var passes = [
'.hb-fill-top::before',
'.hb-fill-top:hover::before',
'.hb-fill-top',
'.hb-fill-top:hover',
'.hb-fill-top::before',
'.hb-fill-top:hover::before',
'.hb-fill-top:hover'
];
var fails = ['.hb-fill-top-bg-br'];
var myClass = '.hb-fill-top';
var regex = new RegExp(`^\\${myClass}(:{1,2}\\w+)*$`);
passes.forEach(p => console.log(regex.test(p)));
console.log('---');
fails.forEach(f => console.log(regex.test(f)));
var regex1 = new RegExp('\\' + myClass + '(?::[^\s]*)?');
var rule1 = string.match(regex1)
This regex select my class, and everething after if it start with : and stop when it meets a whitespace character.
See the regex in action.
Notice also that I added '\\' at the beginning. This is in order to escape the dot in your className. Otherwise it would have matched something else like
ahb-fill-top
.some-other-hb-fill-top
Also be careful about .* it may match something else after (I don't know your set of strings). You might want to be more precise with :{1,2}[\w-()]+ in the last group. So:
var regex1 = new RegExp('\\' + myClass + '(?::{1,2}[\w-()]+)?');

RegExp Weirdness in JavaScript

I have a variable string in my JavaScript code containing a comma delimited list of words and or phrases, for example:
String 1 : “abc, def hij, klm”
String 2 : “abc, def”
I want to insert the word ‘and’ after the last comma in the string to get
String 1 : “abc, def hij, and klm”
String 2 : “abc, and def”
I put together the following code:
// replace the last comma in the list with ", and"
var regEx1 = new RegExp(",(?=[A-z ]*$)" )
var commaDelimList = commaDelimList.replace(regEx1, ", and ");
The problem is that it does not work if the comma delimited string has only two items separated by one comma.
So the results of the above example are
String 1 : ”abc, def hij, and klm”
String 2 : “abc, def”
Why is the RegExp not working and what can I use to get the result I want?
Not sure a regex was the right way to go there...
Why not use LastIndexOf and replace that with your string?
Since this is a relatively straight forward task, a little string manipulation may be beneficial - you'll realize better performance too.
var str = 'abc, def, hij, klm',
index = str.lastIndexOf(','),
JOINER = ', and';
//'abc, def, hij, and klm'
str.slice(0, index) + JOINER + str.slice(index+1);
Haven't tried you one but this works
'abc, def'.replace( /,(?=[A-z ]*$)/, ", and" )

Categories