I'm trying to figure out a regex pattern that allows a string but removes anything that is not a digit, a ., or a leading -.
I am looking for the simplest way of removing any non "number" variables from a string. This solution doesn't have to be regex.
This means that it should turn
1.203.00 -> 1.20300
-1.203.00 -> -1.20300
-1.-1 -> -1.1
.1 -> .1
3.h3 -> 3.3
4h.34 -> 4.34
44 -> 44
4h -> 4
The rule would be that the first period is a decimal point, and every following one should be removed. There should only be one minus sign in the string and it should be at the front.
I was thinking there should be a regex for it, but I just can't wrap my head around it. Most regex solutions I have figured out allow the second decimal point to remain in place.
You can use this replace approach:
In the first replace we are removing all non-digit and non-DOT characters. Only exception is first hyphen that we negative using a lookahead.
In the second replace with a callback we are removing all the DOT after first DOT.
Code & Demo:
var nums = ['..1', '1..1', '1.203.00', '-1.203.00', '-1.-1', '.1', '3.h3',
'4h.34', '4.34', '44', '4h'
]
document.writeln("<pre>")
for (i = 0; i < nums.length; i++)
document.writeln(nums[i] + " => " + nums[i].replace(/(?!^-)[^\d.]+/g, "").
replace(/^(-?\d*\.\d*)([\d.]+)$/,
function($0, $1, $2) {
return $1 + $2.replace(/[.]+/g, '');
}))
document.writeln("</pre>")
A non-regex solution, implementing a trivial single-pass parser.
Uses ES5 Array features because I like them, but will work just as well with a for-loop.
function generousParse(input) {
var sign = false, point = false;
return input.split('').filter(function(char) {
if (char.match(/[0-9]/)) {
return sign = true;
}
else if (!sign && char === '-') {
return sign = true;
}
else if (!point && char === '.') {
return point = sign = true;
}
else {
return false;
}
}).join('');
}
var inputs = ['1.203.00', '-1.203.00', '-1.-1', '.1', '3.h3', '4h.34', '4.34', '4h.-34', '44', '4h', '.-1', '1..1'];
console.log(inputs.map(generousParse));
Yes, it's longer than multiple regex replaces, but it's much easier to understand and see that it's correct.
I can do it with a regex search-and-replace. num is the string passed in.
num.replace(/[^\d\-\.]/g, '').replace(/(.)-/g, '$1').replace(/\.(\d*\.)*/, function(s) {
return '.' + s.replace(/\./g, '');
});
OK weak attempt but seems fine..
var r = /^-?\.?\d+\.?|(?=[a-z]).*|\d+/g,
str = "1.203.00\n-1.203.00\n-1.-1\n.1\n3.h3\n4h.34\n44\n4h"
sar = str.split("\n").map(s=> s.match(r).join("").replace(/[a-z]/,""));
console.log(sar);
Related
I have strings containing 5 letters of alphabet. I would like to match those that contain letters that are consecutive in alphabet for example:
abcde - return match
nopqrs - return match
cdefg - return match
fghij - return match
but
abcef - do not return match
abbcd - do not return match
I could write all combinations but as you can write in Regex [A-Z] I assumed there must be a better way.
A very simple alternative would be to just use String.prototype.includes:
function isConsecutive(string) {
const result = 'abcdefghijklmnopqrstuvwxyz'.includes(string);
console.log(string, result);
}
// true
isConsecutive('abcde');
isConsecutive('nopqrs');
isConsecutive('cdefg');
isConsecutive('fghij');
// false
isConsecutive('abcef');
isConsecutive('abbcd');
If you can live with Python, this function converts the string sequence into numbered characters, and checks if they are consequtive (if so, they are also consecutive alphabetically):
def are_letters_consequtive(text):
nums = [ord(letter) for letter in text]
if sorted(nums) == list(range(min(nums), max(nums)+1)):
return "match"
return "no match"
print(are_letters_consequtive('abcde'))
print(are_letters_consequtive('cdefg'))
print(are_letters_consequtive('fghij'))
print(are_letters_consequtive('abcef'))
print(are_letters_consequtive('abbcd'))
print(are_letters_consequtive('noprst'))
Outputs:
match
match
match
no match
no match
no match
An alternative using javascript:
let string1 = 'abcde'
let string2 = 'fghiz'
function conletters(string) {
if(string.length > 5 || typeof string != 'string') throw '[ERROR] not string or string greater than 5'
for(let i = 0; i < string.length - 1; i++) {
if(!(string.charCodeAt(i) + 1 == string.charCodeAt(i + 1)))
return false
}
return true
}
console.log('string1 is consecutive: ' + conletters(string1))
console.log('string2 is consecutive: ' + conletters(string2))
You should definitely do it with code:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
That said, you can do better than testing all the combinations when using regexes. With lookahead expressions you can basically do "and" operation. Since you know the length you could do:
const myRegex = /(?=^(ab|bc)...$)(?=^.(ab|bc)..$)(?=^..(ab|bc).$)(?=^...(ab|bc)$)/
You will need to replace the (ab|bc) with all the possible two combinations.
For this particular case it is actually worse than testing all the possibilities (since there are only 22 possibilities) but it makes it more extensible to other situations.
I need to write a solution in JavaScript to replace all the characters in a string with a *, except for the first and last characters. I'm not very familiar with RegEx but was trying to use the following to achieve the solution:
var regex = /\.(?=^)(?!=$)/;
const censored = w.replace(regex)
console.log(censored)
Any ideas on how I can achieve this?
The idea of using lookaheads is viable, let's correct a few mistakes:
var regex = /(?<!^).(?!$)/g;
var w = 'fork'
var censored = w.replace(regex, '*')
console.log(censored)
Do note, however, that lookbehinds (?<= and ?<!) are from ES 2018 and not universally supported yet. (As pointed out in another answer, you actually don't need a lookbehind here, a lookahead (?!^) would do as well). Stil...
You can also chop off the first char and replace the rest:
var w = 'fork'
var censored = w[0] + w.slice(1).replace(/.(?!$)/g, '*')
console.log(censored)
Finally, here's a way to do that without any regexes at all:
var w = 'fork'
var censored = w[0] + '*'.repeat(w.length - 2) + w.slice(-1)
console.log(censored)
Here's a way without regex:
function censor(str) {
return str[0] + new Array(str.length - 2).join('*') + str[str.length - 1]
}
console.log(censor('happy birthday'))
It is very easy with an ECMAScript 5 compliant regex pattern:
var regex = /(?!^)[\s\S](?!$)/g;
var w = "Text!"
var censored = w.replace(regex, "*")
console.log(censored)
Details
(?!^) - negative lookahead: matches a location that is not a string start position
[\s\S] - any char (even a newline)
(?!$) - negative lookahead: matches a location that is not a string end position
See the regex demo.
You could use a replace callback as the second parameter to replace the items like this:
const str = 'fork'
var result = str.replace(/^(.)(.+)(.)$/, (whole, first, middle, last) => {
return first + new Array(middle.length).fill('*').join('') + last
})
console.log(result)
Here's a solution if regex is not a requirement.
function censor(input){
return input.split("").map(function(char, index){
if(index === 0 || index === (input.length - 1)){
return char;
} else {
return "*";
}
}).join("");
}
console.log(censor("Hello world"));
You can also use the following code:
(?<=\w{1})\w(?=\w{1})
where ?<= is positive lookbehind, and ?= is a positive lookahead
I attempted the CoderByte - Simple Symbols - challenge in JavaScript. From CoderByte:
Using the JavaScript language, have the function SimpleSymbols(str)
take the str parameter being passed and determine if it is an
acceptable sequence by either returning the string true or false. The
str parameter will be composed of + and = symbols with several letters
between them (ie. ++d+===+c++==a) and for the string to be true each
letter must be surrounded by a + symbol. So the string to the left
would be false. The string will not be empty and will have at least
one letter.
My solution:
function simpleSymbols(str) {
var isSymbol = true;
var output = " ";
var symbol = " ";
if (str.match(/[a-zA-Z]/).length != 0) {
for (var i = 0; i <= str.length - 1; i++) {
if ((str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') ||
(str.charAt(i) >= 'a' && str.charAt(i) <= 'z')) {
if (i != str.length - 1) {
symbol = str[--i] + str[++i] + str[++i];
var rgx = new RegExp(/\+[a-zA-Z]\+/);
if (!(rgx.test(symbol))) {
isSymbol = false;
break;
}
}
else {
isSymbol = false;
break;
}
}
}
}
else {
isSymbol = false;
}
return isSymbol;
}
This worked fine for all test cases.
On reviewing code of other submissions, I came across a submission which required only a single line of code:
return ('=' + str + '=').match(/([^\+][a-z])|([a-z][^\+])/gi) === null;
I'm having trouble understanding how the RegEx used here works. Theoretically, I understand:
g modifier => checks for all matches
i modifier => case-insensitive checking
a-z => checks the string contains only letters
\+ => refers to the plus sign
| => match either alternative1 OR alternative2
Thus, if referring to the above, I understand that there are two match conditions:
([^\+][a-z])
([a-z][^\+])
So, for a test input such as "+x+y+z+". Am I correct in understanding that the way it checks matches is as follows: +x => x+ => +y => y+ => +z => z+
Further clarification on this RegEx would be really helpful.
Thanks.
https://regex101.com/ is your friend !
Technically you are right about what you have said.
[^+] matches everything BUT the plus sign. Now the regex says "if there is a letter that is not preceded by a + or a letter that is not followed by a plus, return the regex".
But since there is "=== null", it will return true only if the above regex has not found anything.
[^\+] means any character that's not a plus. [] is a character group and a ^ at the beginning inside a character group means negate/not. It just says "does this string contain any character that's not a plus, followed by a letter a-z?" which would mean it doesn't follow the rules.
i been looking everywhere for a solution but nothing which quiet matches my criteria.
i want to enable the user to input any positive numbers with (optional 2 decimal places separated by a dot '.')
i.e: 1.23, 12.23, 123.23, 1234.23, 1, 0
how can i do this?
i attempted to use this regex
$('form').submit(function(){
var amount = $('#amount');
var regex = new RegExp("^\d+(\.\d{1,2})?$");
var error = 0;
if(amount.val() == '' || amount.val() <= 0 || regex.test(amount.val()) == false){
$('label', amount.parent()).addClass('error');
error++;
}else{
$('label', amount.parent()).removeClass('error');
}
if(error > 0){
return false;
}
});
i am not very experienced with regex, and attempted to use the code above which i found through my search but it doesn't seem to work and i don't know why. i am not sure if it is an error in the regex or the way i am using it.
Use var regex = new RegExp("^[-+]?\d+(\.\d\d?)?$"); for 2 decimal places(1st compulsory, 2nd not).
Something like:
^\d+(\.\d{1,2})?$
Note we dropped the sign part [-+]? because you stated only positive numbers.
Explanation:
^ start of string
\d+ match zero or more digits
(\.\d{0,2})? match the pattern in the brackets 0 or 1 time
\. match a literal .
\d{1,2) match 1 or 2 digits
$ end of string
So this allows some digits optionally followed by a . and 1 or 2 more digits. Adjust as needed.
var rx = /^\d+(\.\d{1,2})?$/;
alert(rx.test("1.22") + "\n" // true
+ rx.test("10") + "\n" // true
+ rx.test("2223.4") + "\n" // true
+ rx.test("foo") + "\n" // false
+ rx.test("1..22") + "\n" // false
+ rx.test(" 1")); // false
The regex you've posted allows any number of decimal places. This one only allows 2 decimal places
var regex = new RegExp("^[-+]?\d+(\.\d\d?)?$");
It's long and ugly and I don't remember where I initially got it but this is the one I use (it validates proper comma separation as well, and allows for optional $ at the beginning):
var regex = /^\$?(([1-9][0-9]{0,2}(,[0-9]{3})*)|[0-9]+)?(.[0-9]{2})?$/;
Here's a working fiddle: http://jsfiddle.net/5v9zcxrm/5/
For the decimal places I use (.[0-9]{2}) to only validate 2 decimal places or zero. if you want to allow one decimal place use:
var regex = /^\$?(([1-9][0-9]{0,2}(,[0-9]{3})*)|[0-9]+)?(.[0-9]{1,2})?$/;
If all you need is numbers (with no commas) and a possible decimal of one or two digits use:
var regex = /^\d+(.[0-9]{1,2})?$/;
$('form').submit(function(){
var amount = $('#amount');
var regex = /^\d+(\.\d{2,2})?$/;
var error = 0;
if(amount.val() == '' || amount.val() <= 0 || regex.test(amount.val()) == false){
$('label', amount.parent()).addClass('error');
error++;
}else{
$('label', amount.parent()).removeClass('error');
}
if(error > 0){
return false;
}
});
this is my updated solution which works as intended. special thanks to Matt Burland and everyone who made this possible
I made this helper function to find single words, that are not part of bigger expressions
it works fine on any word that is NOT first or last in a sentence, why is that?
is there a way to add "" to regexp?
String.prototype.findWord = function(word) {
var startsWith = /[\[\]\.,-\/#!$%\^&\*;:{}=\-_~()\s]/ ;
var endsWith = /[^A-Za-z0-9]/ ;
var wordIndex = this.indexOf(word);
if (startsWith.test(this.charAt(wordIndex - 1)) &&
endsWith.test(this.charAt(wordIndex + word.length))) {
return wordIndex;
}
else {return -1;}
}
Also, any improvement suggestions for the function itself are welcome!
UPDATE: example: I want to find the word able in a string, I waht it to work in cases like [able] able, #able1 etc.. but not in cases that it is part of another word like disable, enable etc
A different version:
String.prototype.findWord = function(word) {
return this.search(new RegExp("\\b"+word+"\\b"));
}
Your if will only evaluate to true if endsWith matches after the word. But the last word of a sentence ends with a full stop, which won't match your alphanumeric expression.
Did you try word boundary -- \b?
There is also \w which match one word character ([a-zA-Z_]) -- this could help you too (depends on your word definition).
See RegExp docs for more details.
If you want your endsWith regexp also matches the empty string, you just need to append |^$ to it:
var endsWith = /[^A-Za-z0-9]|^$/ ;
Anyway, you can easily check if it is the beginning of the text with if (wordIndex == 0), and if it is the end with if (wordIndex + word.length == this.length).
It is also possible to eliminate this issue by operating on a copy of the input string, surrounded with non-alphanumerical characters. For example:
var s = "#" + this + "#";
var wordIndex = this.indexOf(word) - 1;
But I'm afraid there is another problems with your function:
it would never match "able" in a string like "disable able enable" since the call to indexOf would return 3, then startsWith.test(wordIndex) would return false and the function would exit with -1 without searching further.
So you could try:
String.prototype.findWord = function (word) {
var startsWith = "[\\[\\]\\.,-\\/#!$%\\^&\*;:{}=\\-_~()\\s]";
var endsWith = "[^A-Za-z0-9]";
var wordIndex = ("#"+this+"#").search(new RegExp(startsWith + word + endsWith)) - 1;
if (wordIndex == -1) { return -1; }
return wordIndex;
}