How do I go backwards a letter? - ruby

Using next, I created a method that encrypts a password by advancing every letter of a string one letter forward:
def encryptor
puts "Give me your password!"
password = gets.chomp
index = 0
while index < password.length
password[index] = password[index].next!
index +=1
end
puts password
end
encryptor
I have to create a decrypt method that undoes that. In the end, this should be cleared:
encrypt("abc") should return "bcd"
encrypt("zed") should return "afe"
decrypt("bcd") should return "abc"
decrypt("afe") should return "zed"
I see that Ruby does not have a method to go backwards. I'm stuck with reversing letters. I tried to add an alphabet to index within the method, but I can't get it to do it.
Any help in the right direction would be greatly appreciated.

I know that you can use .next to advance in a string.
Well, kind of, but there are special cases you have to be aware of:
'z'.next #=> 'aa'
I did this successfully
Not quite, your encryptor maps "xyz" to "yzab".
I see that Ruby does not have this option to just go backwards.
Take this example:
'9'.next #=> '10'
'09'.next #=> '10'
As you can see, the mapping is not injective. Both, '9' and '09' are mapped to '10'. Because of this, there is no String#pred – what should '10'.pred return?
Now I'm completely stuck with reversing it a letter.
You could use tr: (both, for encryption and decryption)
'abc'.tr('abcdefghijklmnopqrstuvwxyz', 'zabcdefghijklmnopqrstuvwxy')
#=> 'zab'
tr also has a c1-c2 notation for character ranges, so it can be shortened to:
'abc'.tr('a-z', 'za-y')
#=> 'zab'
Or via Range#to_a, join and rotate:
from = ('a'..'z').to_a.join #=> "abcdefghijklmnopqrstuvwxyz"
to = ('a'..'z').to_a.rotate(-1).join #=> "zabcdefghijklmnopqrstuvwxy"
'abc'.tr(from, to)
#=> "zab"
Another option is to define two alphabets:
from = ('a'..'z').to_a
#=> ["a", "b", "c", ..., "x", "y", "z"]
to = from.rotate(-1)
#=> ["z", "a", "b", ..., "w", "x", "y"]
And create a hash via zip:
hash = from.zip(to).to_h
#=> {"a"=>"z", "b"=>"a", "c"=>"b", ..., "x"=>"w", "y"=>"x", "z"=>"y"}
Which can be passed to gsub:
'abc'.gsub(/[a-z]/, hash)
#=> "zab"
You can also build the regular expression programmatically via Regexp::union:
Regexp.union(hash.keys)
#=> /a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z/

You can use the .next to do this as long as you test for z:
> 'abc'.split("").map { |ch| ch=='z' ? 'a' : ch.next }.join
=> "bcd"
> 'zed'.split("").map { |ch| ch=='z' ? 'a' : ch.next }.join
=> "afe"
Then for decrypt you can do:
> "bcd".split("").map { |ch| ch=='a' ? 'z' : (ch.ord-1).chr }.join
=> "abc"
> "afe".split("").map { |ch| ch=='a' ? 'z' : (ch.ord-1).chr }.join
=> "zed"
Best

Related

Generate a hash of all letters and digits

Using ruby, how do I make a hash of each letter in the alphabet (keys) and 1-26 (values) ?
I need to create a hash with "a" to "z" in keys and 1 to 26 in values but I do not want to write myself alphabet = {'a'=>1,'b'=>2,....'y'=>25,'z'=>26}
I need this in my code to print alphabet[i] if alphabet.key?(i)
('a'..'z').each.with_index(1).to_h
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10,
# "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19,
# "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}
Steps:
('a'..'z') - create a Range of alphabetic letters "a" through "z" inclusive
each - returns an Enumerator
with_index(1) - returns an Enumerator of each element of the initial Range combined with its index (starting at 1) e.g. [["a",1],["b",2],...]
to_h - convert the Enumerator to a Hash
Update:
A bit more esoteric but this will also work
enum = Enumerator.produce('a') {|e| e == 'z' ? raise(StopIteration) : e.succ }.tap do |e|
e.define_singleton_method(:[]) {|elem| find_index(elem)&.+(1) }
e.define_singleton_method(:to_h) { with_index(1).to_h }
end
enum['w']
#=> 23
enum['W']
#=> nil
enum.to_h
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10,
# "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19,
# "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}
With two ranges, zip and to_h
('a'..'z').zip(1..26).to_h
Hash[('a'..'z').zip(1.upto(26))]
Depending on requirements you may be able to save memory by using an empty hash with a default proc.
h = Hash.new do |_h,k|
k.is_a?(String) && k.match?(/\A[a-z]\z/) ? (k.ord - 96) : nil
end
#=> {}
h['a'] #=> 1
h['z'] #=> 26
h['R'] #=> nil
h['cat'] #=> nil
h[2] #=> nil
h[{a:1}] #=> nil
See Hash::new and String#match?.
The regular expression reads, "match the beginning of the string (\A) followed by one lowercase letter ([a-z]) followed by the end of the string (\z). [a-z] denotes a character class.
If all lowercase letters must comprise the hash's keys one may write the following.
('a'..'z').to_h { |c| [c, c.ord - 96] }
#=> {"a"=>1, "b"=>2,..., "y"=>25, "z"=>26}
See Enumerable#to_h.
There have been better answers given already, but here's an entirely different option using a times loop to simply increment the keys and values of a starter hash using next:
h = {"a" => 1}
25.times {h[h.keys.last.next] = h.values.last.next}
h
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10, "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19, "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}

Ruby string char chunking

I have a string "wwwggfffw" and want to break it up into an array as follows:
["www", "gg", "fff", "w"]
Is there a way to do this with regex?
"wwwggfffw".scan(/((.)\2*)/).map(&:first)
scan is a little funny, as it will return either the match or the subgroups depending on whether there are subgroups; we need to use subgroups to ensure repetition of the same character ((.)\1), but we'd prefer it if it returned the whole match and not just the repeated letter. So we need to make the whole match into a subgroup so it will be captured, and in the end we need to extract just the match (without the other subgroup), which we do with .map(&:first).
EDIT to explain the regexp ((.)\2*) itself:
( start group #1, consisting of
( start group #2, consisting of
. any one character
) and nothing else
\2 followed by the content of the group #2
* repeated any number of times (including zero)
) and nothing else.
So in wwwggfffw, (.) captures w into group #2; then \2* captures any additional number of w. This makes group #1 capture www.
You can use back references, something like
'wwwggfffw'.scan(/((.)\2*)/).map{ |s| s[0] }
will work
Here's one that's not using regex but works well:
def chunk(str)
chars = str.chars
chars.inject([chars.shift]) do |arr, char|
if arr[-1].include?(char)
arr[-1] << char
else
arr << char
end
arr
end
end
In my benchmarks it's faster than the regex answers here (with the example string you gave, at least).
Another non-regex solution, this one using Enumerable#slice_when, which made its debut in Ruby v.2.2:
str.each_char.slice_when { |a,b| a!=b }.map(&:join)
#=> ["www", "gg", "fff", "w"]
Another option is:
str.scan(Regexp.new(str.squeeze.each_char.map { |c| "(#{c}+)" }.join)).first
#=> ["www", "gg", "fff", "w"]
Here the steps are as follows
s = str.squeeze
#=> "wgfw"
a = s.each_char
#=> #<Enumerator: "wgfw":each_char>
This enumerator generates the following elements:
a.to_a
#=> ["w", "g", "f", "w"]
Continuing
b = a.map { |c| "(#{c}+)" }
#=> ["(w+)", "(g+)", "(f+)", "(w+)"]
c = b.join
#=> "(w+)(g+)(f+)(w+)"
r = Regexp.new(c)
#=> /(w+)(g+)(f+)(w+)/
d = str.scan(r)
#=> [["www", "gg", "fff", "w"]]
d.first
#=> ["www", "gg", "fff", "w"]
Here's one more way of doing it without a regex:
'wwwggfffw'.chars.chunk(&:itself).map{ |s| s[1].join }
# => ["www", "gg", "fff", "w"]

Ruby: Insert Multiple Values Into String

Suppose we have the string "aaabbbccc" and want to use the String#insert to convert the string to "aaa<strong>bbb</strong>ccc". Is this the best way to insert multiple values into a Ruby string using String#insert or can multiple values simultaneously be added:
string = "aaabbbccc"
opening_tag = '<strong>'
opening_index = 3
closing_tag = '</strong>'
closing_index = 6
string.insert(opening_index, opening_tag)
closing_index = 6 + opening_tag.length # I don't really like this
string.insert(closing_index, closing_tag)
Is there a way to simultaneously insert multiple substrings into a Ruby string so the closing tag does not need to be offset by the length of the first substring that is added? I would like something like this one liner:
string.insert(3 => '<strong>', 6 => '</strong>') # => "aaa<strong>bbb</strong>ccc"
Let's have some fun. How about
class String
def splice h
self.each_char.with_index.inject('') do |accum,(c,i)|
accum + h.fetch(i,'') + c
end
end
end
"aaabbbccc".splice(3=>"<strong>", 6=>"</strong>")
=> "aaa<strong>bbb</strong>ccc"
(you can encapsulate this however you want, I just like messing with built-ins because Ruby lets me)
How about inserting from right to left?
string = "aaabbbccc"
string.insert(6, '</strong>')
string.insert(3, '<strong>')
string # => "aaa<strong>bbb</strong>ccc"
opening_tag = '<strong>'
opening_index = 3
closing_tag = '</strong>'
closing_index = 6
string = "aaabbbccc"
string[opening_index...closing_index] =
opening_tag + string[opening_index...closing_index] + closing_tag
#=> "<strong>bbb</strong>"
string
#=> "aaa<strong>bbb</strong>ccc"
If your string is comprised of three groups of consecutive characters, and you'd like to insert the opening tag between the first two groups and the closing tag between the last two groups, regardless of the size of each group, you could do that like this:
def stuff_tags(str, tag)
str.scan(/((.)\2*)/)
.map(&:first)
.insert( 1, "<#{tag}>")
.insert(-2, "<\/#{tag}>")
.join
end
stuff_tags('aaabbbccc', 'strong') #=> "aaa<strong>bbb</strong>ccc"
stuff_tags('aabbbbcccccc', 'weak') #=> "aa<weak>bbbb</weak>cccccc"
I will explain the regex used by scan, but first would like to show how the calculations proceed for the string 'aaabbbccc':
a = 'aaabbbccc'.scan(/((.)\2*)/)
#=> [["aaa", "a"], ["bbb", "b"], ["ccc", "c"]]
b = a.map(&:first)
#=> ["aaa", "bbb", "ccc"]
c = b.insert( 1, "<strong>")
#=> ["aaa", "<strong>", "bbb", "ccc"]
d = c.insert(-2, "<\/strong>")
#=> ["aaa", "<strong>", "bbb", "</strong>", "ccc"]
d.join
#=> "aaa<strong>bbb</strong>ccc"
We need two capture groups in the regex. The first (having the first left parenthesis) captures the string we want. The second captures the first character, (.). This is needed so that we can require that it be followed by zero or more copies of that character, \2*.
Here's another way this can be done:
def stuff_tags(str, tag)
str.chars.chunk {|c| c}
.map {|_,a| a.join}
.insert( 1, "<#{tag}>")
.insert(-2, "<\/#{tag}>")
.join
end
The calculations of a and b above change to the following:
a = 'aaabbbccc'.chars.chunk {|c| c}
#=> #<Enumerator: #<Enumerator::Generator:0x000001021622d8>:each>
# a.to_a => [["a",["a","a","a"]],["b",["b","b","b"]],["c",["c","c","c"]]]
b = a.map {|_,a| a.join }
#=> ["aaa", "bbb", "ccc"]

Strip in collect on splitted array in Ruby

The following code:
str = "1, hello,2"
puts str
arr = str.split(",")
puts arr.inspect
arr.collect { |x| x.strip! }
puts arr.inspect
produces the following result:
1, hello,2
["1", " hello", "2"]
["1", "hello", "2"]
This is as expected. The following code:
str = "1, hello,2"
puts str
arr = (str.split(",")).collect { |x| x.strip! }
puts arr.inspect
Does however produce the following output:
1, hello,2
[nil, "hello", nil]
Why do I get these "nil"? Why can't I do the .collect immediately on the splitted-array?
Thanks for the help!
The #collect method will return an array of the values returned by each block's call. In your first example, you're modifying the actual array contents with #strip! and use those, while you neglect the return value of #collect.
In the second case, you use the #collect result. Your problem is that #strip! will either return a string or nil, depending on its result – especially, it'll return nil if the string wasn't modified.
Therefore, use #strip (without the exclamation mark):
1.9.3-p194 :005 > (str.split(",")).collect { |x| x.strip }
=> ["1", "hello", "2"]
Because #strip! returns nil if the string was not altered.
In your early examples you were not using the result of #collect, just modifying the strings with #strip!. Using #each in that case would have made the non-functional imperative loop a bit more clear. One normally uses #map / #collect only when using the resulting new array.
You last approach looks good, you wrote a functional map but you left the #strip! in ... just take out the !.

How can I pull a string from an array and then check the first letter of it?

puts "Please Enter First Initial..."
initial = gets
first_letter( name, age, initial)
def first_letter( x, y, z)
index = 0
while index < x.length
--->if z == (x[index])
puts "#{x[index]} #{y[index]}"
end
index += 1
end
end
So essentially what I'm trying to do is use the above code to pull a word from an array and then check the first letter of that string for a match. Essentially it asks the user for a letter and then it checks that letter against the first letter of each string in the array. The marked line is supposed to check the letter against the first letter of the string. If it is equal to the letter, the program is to put the name and age of that entry.
Your question is a bit hard to understand, but the following code selects all the strings from the array where the first letter is an a. Maybe this gets you on the right track:
a #=> ["a", "b", "c", "aa", "bb", "cc", "aaa", "bbb", "ccc"]
a.select { |x| x[0] == ?a } #=> ["a", "aa", "aaa"]
# or
a.select { |x| x.start_with? 'a' } #=> ["a", "aa", "aaa"]
a = %w{ axxx bxxx aaaa cccc azz }
# => ["axxx", "bxxx", "aaaa", "cccc", "azz"]
a.grep(/^a/)
# => ["axxx", "aaaa", "azz"]
Consider Enumerable#grep method with a little bit of regex.

Resources