Array to String with specific format - arrays

I have a hash like this:
#password_constraints = {
length: 'Must be at least 6 character long.',
contain: [
'one number',
'one lowercase letter',
'one uppercase letter.'
]
}
And I want to write a method that returns an human-readable string built from this hash like this one:
Must be at least 6 character long. Must contain: one number, one lowercase letter and an uppercase letter.
Currently, I have a very verbose method with an each that iterates over the #password_constraints[:contain] array and has several conditions to check if I have to put a ,, or an and or nothing.
But I want almost the same behavior as join(', ') but the last delimiter must be and.
I'm looking for some sort of solution like this one with the special join:
def password_constraints
#password_constraints[:length] << ' Must contain.' << #password_constraints[:contain].join(', ')
end
Is there a way to make this more compact or Ruby-like?

You can use the splat operator to split the array and just check the case if there's only one string:
def message(constraints)
*others, second_last, last = constraints[:contain]
second_last += " and #{last}" unless last.nil?
"#{constraints[:length]} Must contain: #{(others << second_last).join(', ')}"
end
#password_constraints = {
length: 'Must be at least 6 character long.',
contain: [
'one number',
'one lowercase letter',
'one uppercase letter.'
]
}
message(#password_constraints)
# => "Must be at least 6 character long. Must contain: one number, one lowercase letter and one uppercase letter."
# if #password_constraints[:contain] = ['one lowercase letter', 'one uppercase letter.']
message(#password_constraints)
# => "Must be at least 6 character long. Must contain: one lowercase letter and one uppercase letter."
# if #password_constraints[:contain] = ['one lowercase letter']
message(#password_constraints)
# => "Must be at least 6 character long. Must contain: one lowercase letter"

I would use a monkey patch here for succinctness (yeah my join_requirements method could probably look a little nicer).
class Array
def join_requirements
result = ""
self.each do |requirement|
if requirement == self.last
result << "and " << requirement
else
result << requirement + ", "
end
end
result
end
end
Then in your code you just see this:
contain = ['one number','one lowercase letter','one uppercase letter.']
puts contain.join_requirements
# => one number, one lowercase letter, and one uppercase letter.

Here's a fun little shorthand trick in Ruby that you can do with arrays to cut out the need for a verbose join.
def password_constraints
#password_constraints[:length] << ' Must contain.' << #password_constraints[:contain] * ", "
end
The Array class' * operator explicitly checks for a string second operand, then converts to calling join under the covers.

Related

Kotlin - How to split the string into digits and string?

I want to split a long string (containing digits and characters) into different substrings in Kotlin?
eg:- Buy these 2 products and get 100 off
output needed -> "Buy these ","2 ","products and get ","100 ","off"
Use a regular expression that will match either a group of digits, or a group of non-digit characters.
I used \d+|\D+.
\d+ will match groups of digits, like 2 and 100
\D+ will match any sequence of characters that doesn't contain digits
The | indicates that the pattern should match either \d+ or \D+
Use findAll to find each part of the string that matches the pattern.
val regex = Regex("""\d+|\D+""")
val input = "Buy these 2 products and get 100 off"
val result = regex.findAll(input).map { it.groupValues.first() }.toList()
The result I get is:
["Buy these ", "2", " products and get ", "100", " off"]
The spacing isn't quite the same as what you're looking for, so you could trim the results, or adjust the regex pattern.
this code also works well in this example:
val str = "Buy these 2 products and get 100 off"
val result = str.split(regex = Regex("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)"))

How to loop assigning characters in a string to variable?

I need to take a string and assign each character to a new string variable for a Text To Speech engine to read out each character separately, mainly to control the speed at which it's read out by adding pauses in between each character.
The string contains a number which can vary in length from 6 digits to 16 digits, and I've put the below code together for 6 digits but would like something neater to handle any different character count.
I've done a fair bit of research but can't seem to find a solution, plus I'm new to Groovy / programming.
OrigNum= "12 34 56"
Num = OrigNum.replace(' ','')
sNum = Num.split("(?!^)")
sDigit1 = sNum[0]
sDigit2 = sNum[1]
sDigit3 = sNum[2]
sDigit4 = sNum[3]
sDigit5 = sNum[4]
sDigit6 = sNum[5]
Edit: The reason for needing a new variable for each character is the app that I'm using doesn't let the TTS engine run any code. I have to specifically declare a variable beforehand for it to be read out
Sample TTS input: "The number is [var:sDigit1] [pause] [var:sDigit2] [pause]..."
I've tried using [var:sNum[0]] [var:sNum[1]] to read from the map instead but it is not recognised.
Read this about dynamically creating variable names.
You could use a map in your stuation, which is cleaner and more groovy:
Map digits = [:]
OrigNum.replaceAll("\\s","").eachWithIndex { digit, index ->
digits[index] = digit
}
println digits[0] //first element == 1
println digits[-1] //last element == 6
println digits.size() // 6
Not 100% sure what you need, but to convert your input String to output you could use:
String origNum = "12 34 56"
String out = 'The number is ' + origNum.replaceAll( /\s/, '' ).collect{ "[var:$it]" }.join( ' [pause] ' )
gives:
The number is [var:1] [pause] [var:2] [pause] [var:3] [pause] [var:4] [pause] [var:5] [pause] [var:6]

Improve code with checking element in array is digit

I want to check that each element in String is digit. Firstly, I split the String to an Array by a regexp [, ]+ expression and then I try to check each element by forall and isDigit.
object Test extends App {
val st = "1, 434, 634, 8"
st.split("[ ,]+") match {
case arr if !arr.forall(_.forall(_.isDigit)) => println("not an array")
case arr if arr.isEmpty => println("bad delimiter")
case _ => println("success")
}
}
How can I improve this code and !arr.forall(_.forall(_.isDigit))?
Use matches that requires the string to fully match the pattern:
st.matches("""\d+(?:\s*,\s*\d+)*""")
See the Scala demo and the regex demo.
Details
In a triple quoted string literal, there is no need to double escape backslashes that are part of regex escapes
Anchors - ^ and $ - are implicit when the pattern is used with .matches
The regex means 1+ digits followed with 0 or more repetitions of a comma enclosed with 0 or more whitespaces and then 1+ digits.
I think it can be simplified while also making it a bit more robust.
val st = "1,434 , 634 , 8" //a little messier but still valid
st.split(",").forall(" *\\d+ *".r.matches) //res0: Boolean = true
I'm assuming strings like "1,,,434 , 634 2 , " should fail.
The regex can be put in a variable so that it is compiled only once.
val digits = " *\\d+ *".r
st.split(",").forall(digits.matches)

Regex to reject if all numbers and reject colon

I am trying for a regex to
reject if input is all numbers
accept alpha-neumeric
reject colon ':'
I tried ,
ng-pattern="/[^0-9]/" and
ng-pattern="/[^0-9] [^:]*$/"
for example ,
"Block1 Grand-street USA" must be accepted
"111132322" must be rejected
"Block 1 grand : " must be rejected
You may use
ng-pattern="/^(?!\d+$)[^:]+$/"
See the regex demo.
To only forbid a : at the end of the string, use
ng-pattern="/^(?!\d+$)(?:.*[^:])?$/"
See another regex demo
The pattern matches
^ - start of string
(?!\d+$) - no 1+ digits to the end of the string
[^:]+ - one or more chars other than :
(?:.*[^:])? - an optional non-capturing group that matches 1 or 0 occurrences of
.* - any 0+ chars other than line break chars, as many as possible
[^:] - any char other than : (if you do not want to match an empty string, replace the (?: and )?)
$ - end of string.
According to comments, you want to match any character but colon.
This should do the job:
ng-pattern="/^(?!\d+$)[^:]+$/"

how to ensure a user input contains at least one uppercase, lowercase, symbol, and number in ruby

I want to ensure that a password a user enters is at least 7 characters long and has at least one uppercase letter, one lowercase letter, one number, and one symbol.
The code seems to pass through the if loop until the symbol argument where it gets stuck.
puts "Please enter your desired password: "
password = []
pass_clear = ()
while pass_clear == (nil) do
password = gets.to_s.chomp
if password.length < 7
puts "Your password must contain at least seven characters."
elsif password.count("a-z") == password.length
puts "Your password must contain at least one uppercase character."
elsif password.count("A-Z") == password.length
puts "Your password must contain at least one lowercase character."
elsif password.count("!","#","#","$","%","^","&","*","(",")","_","-","+","=") < 1
puts "Your password must contain at least one symbol."
elsif password.count("0","1","2","3","4","5","6","7","8","9") < 1
puts "Your password must contain at least one number."
else
puts "Thank you, your account is complete."
pass_clear == 1
end
end
This is the output:
Please enter your desired password:
frank
Your password must contain at least seven characters.
Please enter your desired password:
frankie
Your password must contain at least one uppercase character.
Please enter your desired password:
Frankie
Your password must contain at least one symbol.
Please enter your desired password:
Frankie$
Your password must contain at least one symbol.
Please enter your desired password:
And it continues looping through the symbol stage regardless of how many symbols there are.
How can I ensure these symbols are recognized so the loop can finish?
You are quoting each of the symbols which is incorrect. You also have to escape the - and ^ characters
password.count("!##$%\\^&*()_\\-+=")
works for me in this example.
You'll also need to use a range for your numbers like:
password.count("0-9")
The - character is used for the ranges like "a-z" so it has to be escaped, the carat ^ is used to negate a count so:
password.count("^a-z")
would return a count of everything that wasn't in the range of a-z.
This can come in handy as you may want to prevent certain characters from being in your password strings. You could do something like:
password.count("^a-zA-Z!##$%\\^&*()_\\-+=0-9)
This would count any other characters outside what you've defined so you would want to get a zero return value to know they didn't use any forbidden characters.
Some further clarification on ranges in count(). The term "range" should not be confused with the Ruby class "Range". The class of Range uses ".." or "..." for the intervening items. In the count() method the range being considered is the ASCII range from the first character's ASCII number to the second character's ASCII number. That's why in my original typo of A-z it was counting ASCII 65 ("A") to ASCII 122 ("z") which happens to include the characters ASCII 92 to 96 which are not letters but \ ] ^ _ `
One option is to use a regex that contains four positive lookaheads, all of which operate from the beginning of the string.
R = /
(?=.*\p{Ll}) # match lowercase letter
(?=.*\p{Lu}) # match uppercase letter
(?=.*\d) # match digit
(?=.*[#{Regexp.escape("!##$%^&*(,)_+=-")}]) # match special char
/x # free-spacing regex def mode
def password_ok?(str)
str.match?(R)
end
password_ok? "aA1#" #=> true
password_ok? "A1#" #=> false
password_ok? "a1#" #=> false
password_ok? "aA#" #=> false
password_ok? "aA1" #=> false

Resources