Takes user input string and convert it to secret code from hash - ruby

I was given a code challenge of taking a user input and converting that input using the following cipher information:
replace = {
"A" => "Z",
"B" => "Y",
"C" => "X",
"D" => "W",
"E" => "V",
"F" => "U",
"G" => "T",
"H" => "S",
"I" => "R",
"J" => "Q",
"K" => "P",
"L" => "O",
"M" => "N",
"N" => "M",
"O" => "L",
"P" => "K",
"Q" => "K",
"R" => "I",
"S" => "H",
"T" => "G",
"U" => "F",
"V" => "E",
"W" => "D",
"X" => "C",
"Y" => "B",
"Z" => "A"
}
This is my code:
puts "Time to decipher some code: "
input = gets.chomp.upcase
replace.each do |k, v|
input.gsub!(k,v)
end
puts input
In my each block, my cipher doesn't seem to properly convert. Can anyone explain why this might be?

You're iterating through the hash replace rather than the input string. So, if your input = "HELLO"...
Input: HELLO
Input: HVLLO
Input: SVLLO
Input: SVOOO
Input: SVLLL #changing back O to L
Input: HVLLL #changing back S to H
Input: HELLL #changing back V to E
Since you're iterating through replace you will end up switching letters more than once.
My solution to this problem would have been to iterate through each of the letters in input and then replace them according to replace. In order to iterate through input I would have to split it and then eventually join it later into a string.

Your problem is not Ruby-specific. It is a very very very elementary (school) level algorithm mistake. You got your result because the replaced characters can later match to be replaced with something else.
It is reminiscent of a mistake when one has variables a = "foo" and b = "bar" and is trying to switch their values, doing:
b = a
a = b
A typical way to do it correctly is:
input.gsub!(/./, Hash.new{|_, k| k}.merge(replace))

input = gets.chomp.upcase
p input.gsub(/[A-Z]/, replace)
The mysterious /[A-Z]/ part is a regular expression, which searches a string for any uppercase character. gsub then replaces it with what's in the replace hash.

Try something like this:
replace = {
"A" => "Z",
"B" => "Y",
"C" => "X",
"D" => "W",
"E" => "V",
"F" => "U",
"G" => "T",
"H" => "S",
"I" => "R",
"J" => "Q",
"K" => "P",
"L" => "O",
"M" => "N",
"N" => "M",
"O" => "L",
"P" => "K",
"Q" => "K",
"R" => "I",
"S" => "H",
"T" => "G",
"U" => "F",
"V" => "E",
"W" => "D",
"X" => "C",
"Y" => "B",
"Z" => "A"
}
puts "Time to decipher some code: "
input = gets.chomp.upcase
output = ""
input.each_char do |c|
output << replace[c]
end
puts output

Thanks everyone for all of the help! I was able to solve it using many of your posts, but Raman's seemed to be the simplest.
replace = {
"A" => "Z",
"B" => "Y",
"C" => "X",
"D" => "W",
"E" => "V",
"F" => "U",
"G" => "T",
"H" => "S",
"I" => "R",
"J" => "Q",
"K" => "P",
"L" => "O",
"M" => "N",
"N" => "M",
"O" => "L",
"P" => "K",
"Q" => "K",
"R" => "I",
"S" => "H",
"T" => "G",
"U" => "F",
"V" => "E",
"W" => "D",
"X" => "C",
"Y" => "B",
"Z" => "A"
}
puts "Time to decipher some code: "
input = gets.chomp.upcase
output = ""
input.each_char do |c|
output << replace[c]
end
puts output

Related

How can I split Hash keys into 2 or more arrays based on keys which contain same values?

Say I made a Hash like this: ["ab" => "a", "ac" => "a", "cd" => "c", "ce" => "c", "df" => "d"]
and I need to split this into 3 arrays like these: ["ab", "ac"], ["cd", "ce"], ["df"]. Split my Hash into 3 arrays based on keys which has same values.
How can I do this?
you can use group by on a hash and group by the values this will return a hash with the value as a key and all hashes with the same value in an array then transform the values and take the first element in each array of that value (wich would be the key of the original objects)
hash.group_by { |key, value| value }.transform_values(&:first).values
[1] pry(main)> {"ab" => "a", "ac" => "a", "cd" => "c", "ce" => "c", "df" => "d"}.group_by { |key, value| value }.transform_values(&:first).values
=> [["ab", "a"], ["cd", "c"], ["df", "d"]]
Input
a={"ab" => "a", "ac" => "a", "cd" => "c", "ce" => "c", "df" => "d"}
Code
p a.group_by{|_,v|v}
.values
.map{|arr|arr.map(&:first)}
output
[["ab", "ac"], ["cd", "ce"], ["df"]]

Generate a range of special characters with ruby

I'm very new to ruby at the moment but I came from a PHP background and must say that I enjoy doing ruby, alot. It's a really nice language and the community is strict but helpful.
Today I was looking at stackoverflow and checked one of my answers to a question to generate a random string using PHP. I had actually written a script for this so I thought, why not share it!
This script has some modifiers which allow you to choose wether you want to include the following sets
lowercase a-z
[1] + uppercase a-z
[1, 2] + numbers
[1, 2, 3] + special characters
[1, 2, 3, 4] + some crazy voodooh characters
So in this PHP script I physically typed each set into an array e.g.:
$charSubSets = array(
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789',
'!##$%^&*()_+{}|:">?<[]\\\';,.`~',
'µñ©æáßðøäåé®þüúíóö'
);
and this was basically my way of being able to define complexity right there.
Now this looks alright, even in the code but ruby has ranges and ranges are something new and shiny for me to play with so I was thinking of building a random string generator later today just to get some more experience with it.
Now for my question, I know that you can do the following things with a range including:
'a'..'z'
'A'..'Z'
0..9
etc.. But I was thinking, could you also make a range with special characters? as in, only special characters? and if that is possible, would you also be able to do the same for the crazy voodooh?
The reason I'm asking is because there is no example in the docs or anything on SO explaining this.
Check out Range#to_a which is gotten from Enumerable. Note that on the left hand side of the docs it says that Range includes Enumerable, which means that the methods in Enumerable can be called on Ranges. If you can't find a method in a class, see what modules the docs say are included and click on the link to the included module.
Check out Array#shuffle.
Check out Array#join
Check out Array#[], which will take a range as a subscript, so you can take a slice of an array of random characters.
A two dot Range includes the end. A three dot Range doesn't include the end:
p (1...5).to_a #=> [1, 2, 3, 4]
Putting it all together:
chars = (0..9).to_a + ('A'..'z').to_a + ('!'..'?').to_a
10.times do
puts chars.shuffle[0..5].join
end
--output:--
I(m,E.
%_;i(3
rb=_ef
kJrA9n
YA`e.K
89qCji
Ba1x3D
acp)=8
2paq3I
U0>Znm
(Shakespeare will appear there eventually.)
Yes - this is certainly possible.
Fire up your console e.g. irb or pry.
1. for the special characters:
('!'..'?').to_a
# => [
# "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-",
# ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":",
# ";", "<", "=", ">", "?"
# ]
2. for the 'voodooh' characters:
('µ'..'ö').to_a
# => [
# "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á",
# "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î",
# "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö"
# ]
This is trivial to just try tho, the position (and kb index of the key) on your keyboard for the end special character defines what characters come inbetween, if I'd pick a ~ instead of a ? for the end it would look like this:
('!'..'~').to_a
# => [
# "`", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",",
# "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
# ":", ";", "<", "=", ">", "?", "#", "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", "[", "\\", "]", "^", "_", "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", "{",
# "|", "}", "~"
# ]
basically if character a is 65 and z is 90 then all characters inbetween like b which is 66 will be included, it works like that for anything you put in a range and since in ruby everything is an object, you can use anything in a range as long as it implements certain methods as explained by the docs!
EDIT (13-11-2015)
After doing some playing around in my console I came to this solution which "mimics" the given PHP example and perhaps even completes it.
def rng(length = 10, complexity = 4)
subsets = [("a".."z"), ("A".."Z"), (0..9), ("!".."?"), ("µ".."ö")]
chars = subsets[0..complexity].map { |subset| subset.to_a }.flatten
# => [
# "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", "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", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "!", "\"",
# "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".",
# "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":",
# ";", "<", "=", ">", "?", "µ", "¶", "·", "¸", "¹", "º", "»",
# "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç",
# "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó",
# "Ô", "Õ", "Ö"
# ]
chars.sample(length).join
end
Now calling rng will produce results like this:
rng # => "·boÇE»Ñ¼Á¸"
rng(10, 2) # => "nyLYAsxJi9"
rng(20, 2) # => "EOcQdjZa0t36xCN8TkoX"
EDIT#2 (14-05-2020)
As pointed out below in the comments, I did not even provide a documentation link to the relevant concept, in Ruby this is called a Range and can be found here (2.5.0).
If you need docs for your specific version, try googling for ruby range [your ruby version]. You can find out what your version is by running ruby -v in the terminal. Happy rubying :D
all dates are in dd-mm-yyyy format

Regex to check alphanumeric string in ruby

I am trying to validate strings in ruby.
Any string which contains spaces,under scores or any special char should fail validation.
The valid string should contain only chars a-zA-Z0-9
My code looks like.
def validate(string)
regex ="/[^a-zA-Z0-9]$/
if(string =~ regex)
return "true"
else
return "false"
end
I am getting error:
TypeError: type mismatch: String given.
Can anyone please let me know what is the correct way of doing this?
If you are validating a line:
def validate(string)
!string.match(/\A[a-zA-Z0-9]*\z/).nil?
end
No need for return on each.
You can just check if a special character is present in the string.
def validate str
chars = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a
str.chars.detect {|ch| !chars.include?(ch)}.nil?
end
Result:
irb(main):005:0> validate "hello"
=> true
irb(main):006:0> validate "_90 "
=> false
def alpha_numeric?(char)
if (char =~ /[[:alpha:]]/ || char =~ /[[:digit:]]/)
true
else
false
end
end
OR
def alpha_numeric?(char)
if (char =~ /[[:alnum:]]/)
true
else
false
end
end
We are using regular expressions that match letters & digits:
The above [[:alpha:]] ,[[:digit:]] and [[:alnum:]] are POSIX bracket expressions, and they have the advantage of matching Unicode characters in their category. Hope this helps.
checkout the link below for more options:
Ruby: How to find out if a character is a letter or a digit?
No regex:
def validate(str)
str.count("^a-zA-Z0-9").zero? # ^ means "not"
end
Great answers above but just FYI, your error message is because you started your regex with a double quote ". You'll notice you have an odd number (5) of double quotes in your method.
Additionally, it's likely you want to return true and false as values rather than as quoted strings.
Similar to the very efficient regex-ish approach mentioned already by #steenslag and nearly just as fast:
str.tr("a-zA-Z0-9", "").length.zero?
OR
str.tr("a-zA-Z0-9", "") == 0
One benefit of using tr though is that you could also optionally analyze the results using the same basic formula:
str = "ABCxyz*123$"
rejected_chars = str.tr("a-zA-Z0-9", "")
#=> *$
is_valid = rejected_chars.length.zero?
#=> false
Similar to #rohit89:
VALID_CHARS = [*?a..?z, *?A..?Z, *'0'..'9']
#=> ["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",
# "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",
# "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
def all_valid_chars?(str)
a = str.chars
a == a & VALID_CHARS
end
all_valid_chars?('a9Z3') #=> true
all_valid_chars?('a9 Z3') #=> false
Use .match? in Ruby 2.4+.
Ruby 2.4 introduced a convenient boolean-returning .match? method.
In your case, I would do something like this:
# Checks for any characters other than letters and numbers.
# Returns true if there are none. Returns false if there are one or more.
#
def valid?( string )
!string.match?( /[^a-zA-Z0-9]/ ) # NOTE: ^ inside [] set turns it into a negated set.
end

The length of sample string doesn't match the value I have given it

I have created this silly cipher but I can't get it to work properly..
Let's say i type a single letter "h" and let the script do it's work.
I've created a method which counts the length of an string (in this case hash value) minus a the value of another string (in this case 100). Then the script should create a new string based on whatever 100 minus the hash value length is (as seen below).
When I print the cipher plus the random string (which together creates a "encrypted message"), they don't add up to 100. I think I got like 41 characters. So what's going on here? Why doesn't it work? And how to I solve it?
####
# Counts the characters of the encrypted message and returns
# the number of characters that needs to be generated and added
# to the message so that every encrypted message has a total
# of 100 characters.
def add_random_fill(string)
char_length = 100 - string.size
return ([*('0'..'9'),*('a'..'z')]).sample(char_length.to_i).join
end
cipher = {
"a" => ['1q24t', 'akO17'],
"b" => ['5T15q', 'x1m45'],
"c" => ['1p97x', '9zP23'],
"d" => ['9z22z', '7qM61'],
"e" => ['1r18i', '5ik80'],
"f" => ['3P42e', '9tv12'],
"g" => ['7j80e', '13y25'],
"h" => ['1k51w', 'u6c74'],
"i" => ['6c85c', 'gT399'],
"j" => ['9V36v', 'z1P58'],
"k" => ['3L88j', '0hi92'],
"l" => ['6j11o', 'e7r33'],
"m" => ['1b82y', 'j1k26'],
"n" => ['2y43e', 'kXO91'],
"o" => ['7h48q', 'W1e12'],
"p" => ['1Z10p', 'M9y53'],
"q" => ['9B32o', '5sf48'],
"r" => ['7W77l', 'n3n27'],
"s" => ['3s43k', '20l85'],
"t" => ['7cY5b', '88o93'],
"u" => ['8i14n', '0Ri04'],
"v" => ['9R81s', '4a118'],
"w" => ['1q43a', 'tU081'],
"x" => ["1a02s", "pA323"],
"y" => ["9o00e", "i8j35"],
"z" => ["1j69y", "D7x91"]
}
prompt = ">> "
puts "Enter a message you would like to encrypt.", prompt
input = gets.chomp
encrypted_content = input.gsub(Regexp.union( cipher.keys )) { |m| cipher[m].sample }
output = encrypted_content + add_random_fill(encrypted_content)
puts "\n****************************************************************"
puts "Encrypted message: #{output}"
puts "******************************************************************"
Why doesn't it work?
Your "random" array contains 36 elements:
array = [*('0'..'9'),*('a'..'z')] #=> ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "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"]
array.size #=> 36
sample doesn't return more than that:
array.sample(100).size #=> 36
And how to I solve it?
You could use a loop instead, fetching one random element at a time:
char_length.times.map { array.sample }.join
# or
Array.new(char_length) { array.sample }.join

Ruby - keys of one hash into values of another hash

I have to hashes like so:
hash1 = {
"a" => 1,
"b" => 1,
"c" => 1,
"d" => 1
}
hash2 = {
"1" => 1,
"2" => 1,
"3" => 1,
"4" => 1
}
And I need to merge them so I end up with this:
hash1 = {
"a" => "1",
"b" => "2",
"c" => "3",
"d" => "4"
}
But I don't know where to begin. Help appreciated.
You can try the following:
Hash[hash1.keys.zip(hash2.keys)]
At first, you get array of keys for each hash with hash1.keys and hash2.keys:
["a", "b", "c", "d"]
["1", "2", "3", "4"]
Secondly, you create an array of arrays with hash1.keys.zip(hash2.keys):
[["a", "1"], ["b", "2"], ["c", "3"], ["d", "4"]]
Then with Hash[<...>] you create a Hash where the first value from the first inner array goes as key and the second as value:
{"a"=>"1", "b"=>"2", "c"=>"3", "d"=>"4"}
Example

Resources