I'm looking to randomly change the case of a string to upcase or downcase. There is a similar question on here for it except it doesn't guarantee that the string gets changed.
I am currently using:
def random_case_changes(string_to_change: string, change_case: 'downcase')
raise ArgumentError, "Param is #{string_to_change.class}, not a string" unless string_to_change.is_a? String
string_to_change.chars.map { |char| (rand 0..2).zero? ? char : char.send(change_case) }.join
end
random_case_changes(string_to_change: 'Hello', change_case: 'upcase')
There is a chance that this will just return 'Hello
.
I've looked at using .sample
on the array of chars, but it jumbles the order and I haven't found a way to put the string back into it's original order after a change has been made.
Is there a way to guarantee that a change will take place?
Is there a way to guarantee that a change will take place?
You could get the indices of the character that could change, e.g. the lowercase ones:
string_to_change = 'Hello'
indices = string_to_change.enum_for(:scan, /[[:lower:]]/).map { $~.begin(0) }
#=> [1, 2, 3, 4]
And out of this array pick between 1 and all elements that will change: (the order doesn't matter)
indices_to_be_changed = indices.sample(rand(1..indices.size))
#=> [4, 2]
Now all you have to do is swap the case of the corresponding characters: (upper to lower or vice-versa)
indices_to_be_changed.each do |i|
string_to_change[i] = string_to_change[i].swapcase
end
string_to_change
#=> "HeLlO"
are you able to explain what
{ $~.begin(0) }
is doing please? I am guessing it is mapping starting at zero?I've read some Ruby documentation and I get what it is doing. Thank you, this solution is great.
What about "HELLO"?
@CarySwoveland that case can be handled by checking whether
indices
is empty. I assumed that there’s at least one possible result.Tom, $~ is the MatchData object for the last regex match (
$~.class #=> MatchData)
. Therefore,$~.begin(0)
is element of the match array (here there's but one) at index0
. See MatchData#begin. You can alternatively writeRegexp.last_match.begin(0)
, which is more self-documenting. See Regexp::last_match.