Checking string with minimum 8 digits using regex - ruby

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

Related

Ruby four-digit number, how to check it whether they are two the same numbers

How to check that there are at least two the same values on four-digit number or there aren't at least two the same values
for example 2432 - there are two 2, how to check it and then spit out an information that there are at least two or more the same numbers in this four-digit number?
puts "enter four-digit number: "
four_digit_num = gets.chomp.to_i
You can do that as follows.
r = /(\d)\d*\1/
gets.match?(r) # when gets #=> "1231\n"
#=> true
gets.match?(r) # when gets #=> "1232\n"
#=> true
gets.match?(r) # when gets #=> "1234\n"
#=> false
We can write the regular expression in free-spacing mode to make it self-documenting.
r = /
(\d) # match a digit and save to capture group 1
\d* # match zero or more digits
\1 # match the contents of capture group 1
/x # specify free-spacing regex definition mode
See String#match?.
If you must begin with the integer
four_digit_num = gets.to_i
you could write
arr = four_digit_num.digits
arr.uniq.size < arr.size
or convert it to a string and apply the first method above:
four_digit_num.to_s.match?(r)
Test for unique digits:
four_digit_num = gets.to_i
digits = four_digit_num.digits # leading zeros will disappear
puts "2 or more same digits." if digits != digits.uniq
You can have up to two pairs of duplicates, so that's probably a use case that needs to be handled. In any case, with Ruby 2.7.2 you might use a Hash to count occurrences of each digit in your String using Hash#tally (via Enumerable) like so:
def report_duplicates_in(digits)
dupes = digits.to_s.chars.tally.select { _2 > 1 }
puts "dupliicate digits=>counts for #{digits}: #{dupes}" if dupes.any?
[digits, dupes]
end
# test with some values
%w[1234 2432 2442].map do |four_digit_num|
report_duplicates_in(four_digit_num)
end.to_h
This will print the following to standard output and return a Hash:
dupliicate digits=>counts for 2432: {"2"=>2}
dupliicate digits=>counts for 2442: {"2"=>2, "4"=>2}
#=> {"1234"=>{}, "2432"=>{"2"=>2}, "2442"=>{"2"=>2, "4"=>2}}

Create Password Validator

I’m new to ruby, so I want to challenge myself to make a Password Validator, to make the password is valid or not, it needs to match the following conditions
Have at least 7 characters
Have minimum 2 of the following special character('!', '#', '#', '$',
'%', '&', '*')
Have at least 2 number
So I wrote this:
numberOfSymbol=0
numberOfNumbers=0
totalNumber=0
numberArray=(0..9).to_a
inputPassword=gets.chomp
symbol=['!', '#', '#', '$', '%', '&', '*']
neededHash={}
neededHash.default=0
randomGeneratedVar=[]
pppppp=0
symbol.each do |sym|
if inputPassword.include?(sym)
numberOfSymbol+=1
end
end
numberArray.each do |x|
inputPassword.each_char do |y|
if x.to_s == y
neededHash[y] += 1
randomGeneratedVar = neededHash.values
end
end
end
randomGeneratedVar.each {|z| pppppp+=z}
if numberOfSymbol >= 2 && inputPassword.length >= 7 && pppppp >=2
puts "Strong"
else
puts "Weak"
end
After I ran this through the tester it satisfied 16 cases out of 18 cases, I wonder if you guys can help me to perfect this code.
You do not need these amount of variables to create validator.
I think this looks more simple and easy to understand:
def password_validator(password)
symbols = %w(! # # $ % & *)
counter = 0
symbols.each do |element|
if password.include?(element)
counter += 1
end
end
if password.length >= 7 && password.tr("^0-9 ", "").length >= 2 && counter >= 2
"password is valid"
else
"password not valid"
end
end
Symbols is an array of symbols
Walk on each symbol and initializing include?
Just condition if password length >= 7 AND password numbers length >= 2 AND counter of symbols is >= 2
The ruby way and also simple way
def password_complexity
if password.present? && password =~ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d{2})(?=.*[#$!%*?&]{2})[A-Za-z\d#$!%*?&]{7,}$)/
puts "Strong Password"
else
put "Complexity requirement not met. Please use: 1 uppercase, 1 lowercase, 2 digit and 2 special character, 7 characters"
end
end
If you want you can change regular expression, please find some of the examples
Minimum eight characters, at least one letter and one number:
"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$"
Minimum eight characters, at least one letter, one number and one special character:
"^(?=.*[A-Za-z])(?=.*\d)(?=.*[#$!%*#?&])[A-Za-z\d#$!%*#?&]{8,}$"
Minimum eight characters, at least one uppercase letter, one lowercase letter and one number:
"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$"
Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character:
"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,}$"
Minimum eight and maximum 10 characters, at least one uppercase letter, one lowercase letter, one number and one special character:
"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,10}$"
The Rails way
PASSWORD_FORMAT = /\A
(?=.{7,}) # Must contain 8 or more characters
(?=.*\d) # Must contain a digit
(?=.*[a-z]) # Must contain a lower case character
(?=.*[A-Z]) # Must contain an upper case character
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
validates :password,
presence: true,
length: { in: 15 },
format: { with: PASSWORD_FORMAT },
confirmation: true
I have given just example you can modify as per your requirement, Please read this for more information
https://medium.com/#Timothy_Fell/how-to-set-password-requirements-in-rails-d9081926923b

Ruby, looping through a string deleting groups of characters until a desired output is achieved

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 == "!????!!"

Why doesn't scan(/\w/) count the same as gsub(/\s+/, '').length?

I am trying to count the characters in a text file excluding white spaces. My thought was to use scan; however, the tutorial I am reading uses gsub. There is a difference in output between the two, and I was wondering why. Here are the two code blocks; the gsub version is the one that's giving me the correct output:
total_characters_nospaces = text.gsub(/\s+/, '').length
puts "#{total_characters_nospaces} characters excluding spaces."
And the other one:
chars = 0
totes_chars_no = text.scan(/\w/){|everything| chars += 1 }
puts chars
The opposite of \s is not \w - it is \S.
\w is equivalent to [a-zA-Z0-9_]. It does not include many other characters such as punctuation.
\S is the exact opposite of \s - it includes any character that is not whitespace.
Now that your question has been answered, here are a couple other ways you could do it:
s = "now is the time for all good"
s.count "^\s" # => 22
s.each_char.reduce(0) { |count, c| count + (c =~ /\S/ ? 1 : 0) } # => 22

Regex - First Integer in String (skip float)

**updated
right now I'm doing
a = gets
count = ((a.match(/\d+/)).to_s).to_i.
sample input: 2000 of 3.00
actual output: 2000
sample input: 3.00 of 2000
actual output: 3
objective output: 2000 in both cases (skip float)
"3.00 of 2000"[/(?<![.\d])\d+(?![.\d])/].to_i # => 2000
"2000 of 3.00"[/(?<![.\d])\d+(?![.\d])/].to_i # => 2000
This is one of those cases where you have to know your data. If you know that your input will always have exactly one integer, then the following approach will work:
'3.00 of 2000'.split.select { |e| e =~ /^\d+$/ }.last.to_i
#=> 2000
'2000 of 3.00'.split.select { |e| e =~ /^\d+$/ }.last.to_i
#=> 2000
The idea is to split each line of input into an array, and then select just the array elements that contain nothing but digits. Finally, the last (and hopefully only) element of the array is converted to an integer.
There are many ways this can blow up or fail to achieve the results you want given arbitrary input. However, it certainly works for the specific corpus you provided.
Use the code:
a = gets
a.split(/[\sa-z]+/).select {| v | v !~ /\./ }.last.to_i
# => 2000
No regex but...
'2000 to 3.00'.split.find { |s| s.to_i.to_s == s }.to_i
=> 2000
'3.00 to 2000'.split.find { |s| s.to_i.to_s == s }.to_i
=> 2000
The regex [^0-9.]([0-9]+)[^0-9] will match only numbers adjacent to characters which are not number or dot, and capture the number in the single capture group.
If the numbers can also appear adjacent to beginning or end of string, the fix should be self-evident;
(?:^|[^0-9.])([0-9]+)(?:[^0-9.]|$)
str = '3 of 20.00, +42,31 of 455, -6 of -23.7 .'
str.scan(/(?<![\.\d])(-?\d+)(?!\d*\.)/).flatten.map(&:to_i)
=> [3, 42, 31, 455, -6]
The capture group (-?\d+) consists of one or more digits 0-9, optionally preceded by a minus sign.
(?<![\.\d]) is a negative lookbehind group, meaning the capture group cannot be preceded by a decimal point or digit.
(?!\d*\.)/) is a negative lookahead group, meaning the capture group cannot be followed by zero or more digits followed by a decimal point.
str.scan(/(?<![\.\d])(-?\d+)(?!\d*\.)/) #=> [["3"], ["42"], ["31"], ["455"], ["-6"]], which is why flatten must be applied before conversion to integers.
Initially I tried (?<!\.\d*) as the negative lookbehind group, but that generated an error. The reason: negative lookbehinds cannot be variable-length. I understand the same restriction applies in Perl.
Edit: I somehow overlooked the title of the question. To retrieve just the first integer, either tack .first on the end of str.scan or replace that statement with:
str.match(/(?<![\.\d])(-?\d+)(?!\d*\.)/)[0].to_i
words_containing_non_digits = -> x {x[/\D/]}
p '3.00 of 2000'.split.reject &words_containing_non_digits #=> ["2000"]

Resources