Ruby - keys of one hash into values of another hash - ruby

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

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"]]

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

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

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

Make two dimensional array

My array is ["Mehmet,1,3,0,0,0\n", "Veli,2,1,2,0,2\n", "Ali,0,1,1,0,0\n"].
I want to declare two dimensional array like array[1][2] with first dimension for name and second one for note.
How can I make it?
Text.txt is
Mehmet,1,3,0,0,0
Veli,2,1,2,0,2
Ali,0,1,1,0,0
My code is
filename = "text.txt"
results = []
File.new(filename, "r").each { |line| results << line }
results.inject([]){|ar,s|
ar.concat(s.split(/,/))}
puts results.inspect
To modify the set that you first posted:
data = ["Mehmet,1,3,0,0,0\n", "Veli,2,1,2,0,2\n", "Ali,0,1,1,0,0\n"]
data = data.map {|x| y = x.split(","); [y.delete_at(0), y] }
=> [["Mehmet", ["1", "3", "0", "0", "0\n"]],
["Veli", ["2", "1", "2", "0", "2\n"]],
["Ali", ["0", "1", "1", "0", "0\n"]]]
puts data[0][0]
=> Mehmet
array = File.read("text.txt").split
# => ["Mehmet,1,3,0,0,0", "Veli,2,1,2,0,2", "Ali,0,1,1,0,0"]
array.map { |ar| x, *xs = ar.split(","); [x, xs.join] }
# => [["Mehmet", "13000"], ["Veli", "21202"], ["Ali", "01100"]]
Or, if you don't want to join the second sub-array
array.map { |ar| x, *xs = ar.split(","); [x, xs] }
# => [["Mehmet", ["1", "3", "0", "0", "0"]],
# ["Veli", ["2", "1", "2", "0", "2"]],
# ["Ali", ["0", "1", "1", "0", "0"]]]
2d array means array of arrays. So, simply it can be like:
board = [ [ 1, 2, 3 ],[ 4, 5, 6 ]]
Are you looking for something like this?
array = ["Mehmet,1,3,0,0,0\n", "Veli,2,1,2,0,2\n", "Ali,0,1,1,0,0\n"]
new_array = array.map do |elem|
splited = elem.split(/[,\s]/)
[splited.shift, splited]
end
# => [["Mehmet", ["1", "3", "0", "0", "0"]],
# ["Veli", ["2", "1", "2", "0", "2"]],
# ["Ali", ["0", "1", "1", "0", "0"]]]

Array entries from string don't map to the hash

I am very new to Ruby, and programming in general. Firstly, I have the below code:
hashy = {"a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 6, "f" => 6}
array = ["a", "b", "c"]
string = "df"
array.push (string.split(//))
puts array
test = array.map {|a| hashy.select {|k,v| a == k}}
puts test
This code successfully maps 'a', 'b' and 'c' to the hash, and populates test with the keys and values from the hash.
This always works for a pre-defined array. However if I add to the array from a string (in this case the string "df", or create an array from a string, it no longer maps the array values to the hash, and I can't see why. I've looked at different ways of populating the array with the string values, but each time get the same problem.
As far as I can see "df" should also be mapping to the hash.
Any help would be greatly appreciated.
It's because you pushing string.split(//) array to array as one object, so you have one array element among the numbers in array as result.
array = ["a", "b", "c"]
string = "df"
array.push (string.split(//))
=> ["a", "b", "c", ["d", "f"]]
To avoid this, you can use array concatenation, for example
array = ["a", "b", "c"]
string = "df"
array += string.split(//)
=> ["a", "b", "c", "d", "f"]

Resources