Am looking to match if a particular string has at least one digit, one upper, and one lower excluding symbols '%^&$#' and white spaces. So I am using str.match? I wonder if there is a one liner instead of && .. && .. concatenating expressions like data.match?(/\d+|[[:upper:]]|[[:lower:]]|\w+/) does not seem to be working.
def chkin(data)
if data.match?(/\d+/) \
&& data.match?(/[[:upper:]]+/) \
&& data.match?(/[[:lower:]]+/) \
&& data.match?(/\w+/) \
&& data.length >= 10
return true
else
return false
end
end
Also the \w+ alpha numeric does not work with: "2aA1b%%sdf3123!!" >> True when it should be false.
You can do that with a regular expression containing three positive lookaheads:
r = /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/
"abcDE27f".match?(r) #=> true
"abcde27f".match?(r) #=> false
"ABCDE27F".match?(r) #=> false
"abcDEfgh".match?(r) #=> false
You can add other requirements with additional positive lookaheads. For example, if the string must be between 8 and 10 characters in length, you could add
(?=.{8,10}\z)
If you only want the string to contain alphanumic characters add
(?=\p{Alnum}+\z)
or put \A\p{Alnum}+\z at the end of the regex.
Related
I'm trying to match a number between 400 and 499 anywhere in a string. For example, both:
string = "[401] One too three"
string2 = "Yes 450 sir okay"
should match. Both:
string3 = "[123] Test"
string4 = "This is another string"
should fail.
What's the best way to write the regex? I wrote:
string =~ /\d{3}/
to see if the string contains a three digit integer. How can I see if that's within the range?
If you don't actually need the number afterwords, and just need to determine yes or no the string contains a number in the range of 400-499, you can:
Check that you are at the beginning of a line, or have a non-digit character followed by
the digit '4' followed by
Any 2 digits followed by
the end of a line or a non-digit character
so you end up with a regex looking something like
regex = /(?:^|\D)4\d{2}(?:\D|$)/
or, by using negative look ahead/look behinds:
regex = /(?<!\d)4\d{2}(?!\d)/
and you need step 1 and 4 above to make rule out numbers such as 1400-1499 and 4000-4999 (and other such numbers with more than 3 digits that have 400-499 somewhere buried in them). You can then make use of String#match? in newer ruby versions to get just a simple boolean:
string.match?(regex) # => true
string2.match?(regex) # => true
string3.match?(regex) # => false
string4.match?(regex) # => false
"1400".match?(regex) # => false
"400".match?(regex) # => true
"4000".match?(regex) # => false
"[1400]".match?(regex) # => false
"[400]".match?(regex) # => true
"[4000]".match?(regex) # => false
Fairly simple regex, no need to pull out the match and convert it to an integer if you just need a simple yes or no
def doit(str, rng)
str.gsub(/-?\d+/).find { |s| rng.cover?(s.to_i) }
end
doit "[401] One too three", 400..499 #=> "401"
doit "Yes 450 sir okay", 400..499 #=> "450"
doit "Yes -450 sir okay", -499..400 #=> "-450"
doit "[123] Test", 400..499 #=> nil
doit "This is another string", 400..499 #=> nil
Recall that String#gsub returns an enumerator, when used with a single argument and no block. The enumerator merely generates matches and performs no substitutions. I've found a number of situations, as here, where this form of the method can be used to advantage.
if str may contain the representations of multiple integers within the specified range, and all such are desired, simply replace Enumerable#find with Enumerable#select:
"401, 532 and -126".gsub(/-?\d+/).select { |s| (-127..451).cover?(s.to_i) }
#=> ["401", "-126"]
I recommend using a general regex to first extract the number from each line. Then, use a regular script to check the range:
s = "[404] Yes sir okay"
data = s.match(/\[(\d+)\]/)
data.captures
num = data[1].to_i
if (num >= 400 && num < 500)
print "Match"
else
print "No Match"
end
Demo
The pattern I wrote should actually work to match any number in brackets, anywhere in the string.
Extract the digits with a regex, convert the capture group to integer and ask Ruby if they're between your bounds:
s = "[499] One too three"
lo = 400
hi = 499
puts s =~ /(\d{3})/ && $1.to_i.between?(lo, hi)
I'm trying to use the match method with an argument of a regex to select a valid phone number, by definition, any string with nine digits.
For example:
9347584987 is valid,
(456)322-3456 is valid,
(324)5688890 is valid.
But
(340)HelloWorld is NOT valid and
456748 is NOT valid.
So far, I'm able to use \d{9} to select the example string of 9 digit characters in a row, but I'm not sure how to specifically ignore any character, such as '-' or '(' or ')' in the middle of the sequence.
What kind of Regex could I use here?
Given:
nums=['9347584987','(456)322-3456','(324)5688890','(340)HelloWorld', '456748 is NOT valid']
You can split on a NON digit and rejoin to remove non digits:
> nums.map {|s| s.split(/\D/).join}
["9347584987", "4563223456", "3245688890", "340", "456748"]
Then filter on the length:
> nums.map {|s| s.split(/\D/).join}.select {|s| s.length==10}
["9347584987", "4563223456", "3245688890"]
Or, you can grab a group of numbers that look 'phony numbery' by using a regex to grab digits and common delimiters:
> nums.map {|s| s[/[\d\-()]+/]}
["9347584987", "(456)322-3456", "(324)5688890", "(340)", "456748"]
And then process that list as above.
That would delineate:
> '123 is NOT a valid area code for 456-7890'[/[\d\-()]+/]
=> "123" # no match
vs
> '123 is NOT a valid area code for 456-7890'.split(/\D/).join
=> "1234567890" # match
I suggest using one regular expression for each valid pattern rather than constructing a single regex. It would be easier to test and debug, and easier to maintain the code. If, for example, "123-456-7890" or 123-456-7890 x231" were in future deemed valid numbers, one need only add a single, simple regex for each to the array VALID_PATTERS below.
VALID_PATTERS = [/\A\d{10}\z/, /\A\(\d{3}\)\d{3}-\d{4}\z/, /\A\(\d{3}\)\d{7}\z/]
def valid?(str)
VALID_PATTERS.any? { |r| str.match?(r) }
end
ph_nbrs = %w| 9347584987 (456)322-3456 (324)5688890 (340)HelloWorld 456748 |
ph_nbrs.each { |s| puts "#{s.ljust(15)} \#=> #{valid?(s)}" }
9347584987 #=> true
(456)322-3456 #=> true
(324)5688890 #=> true
(340)HelloWorld #=> false
456748 #=> false
String#match? made its debut in Ruby v2.4. There are many alternatives, including str.match(r) and str =~ r.
"9347584987" =~ /(?:\d.*){9}/ #=> 0
"(456)322-3456" =~ /(?:\d.*){9}/ #=> 1
"(324)5688890" =~ /(?:\d.*){9}/ #=> 1
"(340)HelloWorld" =~ /(?:\d.*){9}/ #=> nil
"456748" =~ /(?:\d.*){9}/ #=> nil
Pattern: (Rubular Demo)
^\(?\d{3}\)?\d{3}-?\d{4}$ # this makes the expected symbols optional
This pattern will ensure that an opening ( at the start of the string is followed by 3 numbers the a closing ).
^(\(\d{3}\)|\d{3})\d{3}-?\d{4}$
On principle, though, I agree with melpomene in advising that you remove all non-digital characters, test for 9 character length, then store/handle the phone numbers in a single/reliable/basic format.
I have a coding problem I solved and want to refactor. I know there has to be a cleaner way of doing what I did.
The goal is to write a method that takes a string of "!" and "?" and reduces the string by eliminating all odd groupings of each symbol.
Example - a string "????!!!" would have an odd grouping of "!!!" because there are three in a row. These would be deleted from the string.
If there is only one "!" or "?" its left because it is not in a group.
Ex -
remove("!????!!!?") answer == "!"
# => ("!????!!!?" --> "!?????" --> "!")
In the first string, the only odd grouping is "!!!", once removed, it leaves a new string with an odd grouping "?????". You remove the next odd grouping so you're left with "!". This fits the desired output.
Another example
remove("!???!!") == ""
# => ("!???!!" --> "!!!" --> "")
Current code:
def remove(s)
arr = [s]
i = 0
until i == arr[0].length
s = s.chars.chunk{|c|c}.map{ |n,a| a.join }.select{|x| x if x.length.even? || x.length <= 1}.join
arr << s
i += 1
end
return arr[-1]
end
My code solves this problem and all test cases. I have a suspicion that my until loop can be removed/refactored so that I could solve this problem in one line and have spent hours trying to figure it out with no luck.
Suppose
str = "???!!!???!"
If we first remove the two groups "???" we are left with "!!!!", which cannot be reduced further.
If we first remove the group "!!!" we are left with "??????!", which cannot be reduced further.
If we are permitted to remove all odd groups of either character without reference to the effect that either has on the other, we obtain !, which cannot be reduced further.
It's not clear what rule is to be used. Here are three possibilities and code to implement each.
I will use the following two regular expressions, and in the first two cases a helper method.
Rq = /
(?<!\?) # do not match a question mark, negative lookbehind
\? # match a question mark
(\?{2})+ # match two question marks one or more times
(?!\?) # do not match a question mark, negative lookahead
/x # free-spacing regex definition mode
which is commonly written /(?<!\?)\?(\?{2})+(?!\?)/.
Similarly,
Rx = /(?<!!)!(!{2})+(?!!)/
def sequential(str, first_regex, second_regex)
s = str.dup
loop do
size = s.size
s = s.gsub(first_regex,'').gsub(second_regex,'')
return s if s.size == size
end
end
I apply each of the three methods below to two example strings:
str1 = "???!!!???!"
str2 = 50.times.map { ['?', '!'].sample }.join
#=> "?!!!?!!!?!??????!!!?!!??!!???!?!????!?!!!?!?!???!?"
Replace all odd groups of "?" then odd groups of "!" then repeat until no further removals are possible
def question_before_exclamation(str)
sequential(str, Rq, Rx)
end
question_before_exclamation str1 #=> "!!!!"
question_before_exclamation str2 #=> "??!??!?!!?!?!!?"
Replace all odd groups of "!" then odd groups of "?" then repeat until no further removals are possible
def exclamation_before_question(str)
sequential(str, Rx, Rq)
end
exclamation_before_question str1 #=> "??????!"
exclamation_before_question str2 #=> "??!????!!?!?!!?!?!!?"
Replace all odd groups of both "?" and "!" then repeat until no further removals are possible
Rqx = /#{Rq}|#{Rx}/
#=> /(?-mix:(?<!\?)\?(\?{2})+(?!\?))|(?-mix:(?<!!)!(!{2})+(?!!))/
def question_and_explanation(str)
s = str.dup
loop do
size = s.size
s = s.gsub(Rqx,'')
return s if s.size == size
end
end
question_and_explanation str1 #=> "!"
question_and_explanation str2 #=> "??!?!!?!?!!?!?!!?"
I don't know the exact Ruby syntax for this, but you could simplify your solution by using regular expressions:
Gather all matches of consecutive characters
if all matches are of even length or 1 exit
Test if matches are an odd length
if an odd length, replace with the empty string
else do nothing
Goto step 1
A solution in Perl would be:
#!perl
use strict;
use warnings;
use feature qw(say);
my $string = '!????!!!?';
sub reduce {
my ($s) = #_;
while ( my #matches = $s =~ m/((.)\2+)/g ) {
last if ! grep { length($_) > 1 && length($_) % 2 == 1 } #matches;
foreach my $match ( #matches ) {
$s =~ s/\Q$match// if length($match) > 1 && length($match) % 2 == 1;
}
}
return $s;
}
say reduce($string);
I could be wrong (this is ruby, after all) but I don't think you'll find a one-liner for this because ruby's utility functions generally aren't recursive. But you can use regex to simplify your logic, at the very least:
def remove(s)
while s =~ /(?<!\!)\!([\!]{2})+(?!\!)/ || s =~ /(?<!\?)\?([\?]{2})+(?!\?)/
s.gsub! /(?<!\!)\!([\!]{2})+(?!\!)/, "" # remove odd !
s.gsub! /(?<!\?)\?([\?]{2})+(?!\?)/, "" # remove odd ?
end
return s
end
To make the regex less mind-boggling, it helps to look at them with 'a' instead of '?' and '!':
/(?<!a)a([a]{2})+(?!a)/ #regex for 'a'
(?<!a) #negative lookbehind: the match cannot start with an 'a'
a([a]{2})+ #the match should be an 'a' followed by 1 or more pairs
(?!a) #negative lookahead: the match cannot end with an 'a'
It should be simple enough with a regular expression replacement
def remove(string)
begin
original = string
string.gsub!(/(\!{3,})|(\?{3,})/) { |s| s.length.even? ? s : '' }
end until original == string
string
end
puts remove("!????!!!?").inspect # answer == "!"
puts remove("!???!!").inspect # answer == ""
puts remove("!????!!").inspect # answer == "!????!!"
How can I check if string contains only floats separated by commas with no space.
Something like below:
str = "0.0687987167581341,0.120311605902415,89.8399554017928,198.151088713489" #true
str = "0.068798716758f1341,0.120311605902415, 89.8399554017928,198.151088713489" #False because of "f" in the first value.
str = "0.0687987167581341 0.120311605902415" # False because of no space and comma.
Basically, how can I check if a string is in the form below:
str = "<value>,<value>,<value>" # where value may only contains, integers, floats.
How about this regexp:
str.split(',').all? {|val| val =~ /\A-?\d+(\.\d+)?\Z/}
If the following matches, then it means that str does not satisfy the condition.
str =~ /[^\d.,]/
So, taking the negation:
re = /[^\d.,]/
"0.0687987167581341,0.120311605902415,89.8399554017928,198.151088713489" !~ re
# => true
"0.068798716758f1341,0.120311605902415, 89.8399554017928,198.151088713489" !~ re
# => false
"0.0687987167581341 0.120311605902415" !~ re
# => false
Edit: I thought the title of the question was pretty clear, but obviously not, as I now see that the string may also contain integers. Whether negative numbers are permitted is left unsaid, but -2.1 is a float and -3 is an integer. In any event, my solution works only for floats, though it would not be difficult to modify it to permit integers as well, but that would be too messy to bother with.
I prefer #Linuxios' solution, but I'll mention another approach in the interest of diversity. If we are not concerned about the possibility of extra leading or trailing zeroes, this should work:
str = "0.0687987167581341,0.120311605902415,89.8399554017928,198.151088713489"
str.split(',').all? { |s| s.to_f.to_s == s.strip }
#=>true
If there be extra leading or trailing zeros, or extra whitespace, I believe this would work:
str.split(',').all? {|s| s.to_f.to_s ==
s.match(/^\s*(-)?\s*?0*?(0.\d+?|[1-9]\d*\.\d+?)0*\s*$/)[1..-1].join
I have regex as follows:
/^(\d|-|\(|\)|\+|\s){12,}$/
This will allow digits, (, ), space. But I want to ensure string contains atleast 8 digits.
Some allowed strings are as follows:
(1323 ++24)233
24243434 43
++++43435++4554345 434
It should not allow strings like:
((((((1213)))
++++232+++
Use Look ahead within your regex at the start..
/^(?=(.*\d){8,})[\d\(\)\s+-]{8,}$/
---------------
|
|->this would check for 8 or more digits
(?=(.*\d){8,}) is zero width look ahead that checks for 0 to many character (i.e .*) followed by a digit (i.e \d) 8 to many times (i.e.{8,0})
(?=) is called zero width because it doesnt consume the characters..it just checks
To restict it to 14 digits you can do
/^(?=([^\d]*\d){8,14}[^\d]*$)[\d\(\)\s+-]{8,}$/
try it here
Here's a non regular expression solution
numbers = ["(1323 ++24)233", "24243434 43" , "++++43435++4554345 434", "123 456_7"]
numbers.each do |number|
count = 0
number.each_char do |char|
count += 1 if char.to_i.to_s == char
break if count > 7
end
puts "#{count > 7}"
end
No need to mention ^, $, or the "or more" part of {8,}, or {12,}, which is unclear where it comes from.
The following makes the intention transparent.
r = /
(?=(?:.*\d){8}) # First condition: Eight digits
(?!.*[^-\d()+\s]) # Second condition: Characters other than `[-\d()+\s]` should not be included.
/x
resulting in:
"(1323 ++24)233" =~ r #=> 0
"24243434 43" =~ r #=> 0
"++++43435++4554345 434" =~ r #=> 0
"((((((1213)))" =~ r #=> nil
"++++232+++" =~ r #=> nil