How to turn an array into the keys of a Hash and another array into the values of the same hash? - ruby

I have 2 arrays:
array1 = ["H", "e", "l", "l", "o"]
array2 = ["o", "l", "l", "e", "H"]
I want the array1 elements to become the keys in a new Hash, and the array2 elements to become the corresponding values in the same Hash. Can someone please suggest how to do this?
Thanks

array1 = ["H", "e", "l", "l", "o"]
array2 = ["o", "l", "l", "e", "H"]
p Hash[array1.zip(array2)]
# >> {"H"=>"o", "e"=>"l", "l"=>"e", "o"=>"H"}

There are two "l"s in the "key" array, while Hashes can't have duplicate keys. Any solution will have either "l" => "e" or "l" => "l", but not both. If that's okay, then #RubyLovely's solution is great. If you want to preserve a mapping for each pair, an Array of Hashes might be appropriate:
array1 = ["H", "e", "l", "l", "o"]
array2 = ["o", "l", "l", "e", "H"]
array1.zip(array2).map{|pair| Hash[*pair]}
# => [{"H"=>"o"}, {"e"=>"l"}, {"l"=>"l"}, {"l"=>"e"}, {"o"=>"H"}]

The solution by #RubyLovely is how I would do it but for the sake of variety here is another solution:
array1.each_with_index.reduce({}) do |memo,(x,i)|
memo[x] = array2[i]; memo
end
# => {"H"=>"o", "e"=>"l", "l"=>"e", "o"=>"H"}

Doing it this way, you would not need to create an intermediate array, and hence would be more effective than doing so.
h = {}
array1.zip(array2){|k, v| h[k] = v}
h # => {"H"=>"o", "e"=>"l", "l"=>"e", "o"=>"H"}

Related

no implicit conversion of String into Integer, simple ruby function not working

When I run this code I get a typeError, but when I do it by hand in the IRB everything seems to be working out okay. I believe the problem lies somewhere in my IF statement but I don't know how to fix it.
numerals = ["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"]
def convertToNumbers(string)
arr = string.downcase.split('')
new_array = []
arr.each do |i|
if (arr[i] =~ [a-z])
numValue = numerals.index(arr[i]).to_s
new_array.push(numValue)
end
end
end
You probably meant
arr[i] =~ /[a-z]/
which matches the characters a through z. What you wrote
arr[i] =~ [a-z]
is constructing an array and trying to compare it using the regex comparison operator, which is a type error (assuming variables a and z are defined).
A few issues. As Tyler pointed out inside of the loop you are still referencing arr when you look to only need to use i. Also, the regex issue Max pointed out is valid as well. The function also will return arr and not the new_array array as that is the result of the for loop output.
I made a few modifications.
def convertToNumbers(string)
numerals = ["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"]
arr = string.downcase.split('')
new_array = []
arr.each do |i|
if (i =~ /[a-z]/)
numValue = numerals.index(i).to_s
new_array.push(numValue)
end
end
new_array.join
end
puts convertToNumbers('abcd');
which prints out '0123'

pushing array to array in ruby have a wrong result

I have problem when pushing array to array,
the result is not as I expected.
I run this code below :
#arr = ["e", "s", "l", "e"]
def permutations(array, i=0)
combined_array = Array.new
(i..array.size-1).each do |j|
array[i], array[j] = array[j], array[i]
puts "ARRAY : #{array}"
combined_array << array.to_a
end
return combined_array
end
permutations(#arr)
I got the output :
ARRAY : ["e", "s", "l", "e"]
ARRAY : ["s", "e", "l", "e"]
ARRAY : ["l", "e", "s", "e"]
ARRAY : ["e", "e", "s", "l"]
=> [["e", "e", "s", "l"], ["e", "e", "s", "l"], ["e", "e", "s", "l"], ["e", "e", "s", "l"]]
Result expected :
ARRAY : ["e", "s", "l", "e"]
ARRAY : ["s", "e", "l", "e"]
ARRAY : ["l", "e", "s", "e"]
ARRAY : ["e", "e", "s", "l"]
=> [["e", "s", "l", "e"], ["s", "e", "l", "e"], ["l", "e", "s", "e"], ["e", "e", "s", "l"]]
How to solve this problem ?
According to the documentation, #to_a called on an Array returns self (the array itself, not a copy).
You are adding the same array to combined_array multiple times.
Change the .to_a to .dup and it will work fine.
I think #GolfWolf has solved your problem.
But you don't have to write such a function to solve your problem in Ruby, Ruby has permutation method which you can use it.
p arr.permutation.to_a
If you want to get first 4 element then you can do this,
p arr.permutation.take(4)

Why can't I sort an array of strings by `count`?

With this code:
line = ("Ignore punctuation, please :)")
string = line.strip.downcase.split(//)
string.select! {|x| /[a-z]/.match(x) }
string.sort_by!{ |x| string.count(x)}
the result is:
["r", "g", "s", "l", "c", "o", "o", "p", "u", "i", "t", "u", "a", "t", "i", "a", "p", "n", "e", "e", "n", "n", "e"]
Does sorting by count not work in this case? Why? Is there a better way to isolate the words by frequency?
By your comment, I suppose that you want to sort characters by frequency and alphabetically. When the only sort_by! criteria is string.count(x), frequency groups with the same number of characters can appear mixed with each other. To sort each group alphabetically you have to add a second criteria in the sort_by! method:
line = ("Ignore punctuation, please :)")
string = line.strip.downcase.split(//)
string.select! {|x| /[a-z]/.match(x) }
string.sort_by!{ |x| [string.count(x), x]}
Then the output will be
["c", "g", "l", "r", "s", "a", "a", "i", "i", "o", "o", "p", "p", "t", "t", "u", "u", "e", "e", "e", "n", "n", "n"]
Let's look at your code line-by-line.
line = ("Ignore punctuation, please :)")
s = line.strip.downcase
#=> "ignore punctuation, please :)"
There's no particular reason to strip here, as you will be removing spaces and punctuation later anyway.
string = s.split(//)
#=> ["i", "g", "n", "o", "r", "e", " ", "p", "u", "n", "c", "t",
# "u", "a", "t", "i", "o", "n", ",", " ", "p", "l", "e", "a",
# "s", "e", " ", ":", ")"]
You've chosen to split the sentence into characters, which is fine, but as I'll mention at the end, you could just use String methods. In any case,
string = s.chars
does the same thing and is arguably more clear. What you have now is an array named string. Isn't that a bit confusing? Let's instead call it arr:
arr = s.chars
(One often sees s and str for names of strings, a and arr for names of arrays, h and hash for names of hashes, and so on.)
arr.select! {|x| /[a-z]/.match(x) }
#=> ["i", "g", "n", "o", "r", "e", "p", "u", "n", "c", "t", "u",
# "a", "t", "i", "o", "n", "p", "l", "e", "a", "s", "e"]
Now you've eliminated all but lowercase letters. You could also write that:
arr.select! {|x| s =~ /[a-z]/ }
or
arr.select! {|x| s[/[a-z]/] }
You are now ready to sort.
arr.sort_by!{ |x| arr.count(x) }
#=> ["l", "g", "s", "c", "r", "i", "p", "u", "a", "o", "t", "p",
# "a", "t", "i", "o", "u", "n", "n", "e", "e", "n", "e"]
This is OK, but it's not good practice to be sorting an array in place and counting the frequency of its elements at the same time. Better would be:
arr1 = arr.sort_by{ |x| arr.count(x) }
which gives the same ordering. Is the resulting sorted array correct? Let's count the number of times each letter appears in the string.
I will create a hash whose keys are the unique elements of arr and whose values are the number of times the associated key appears in arr. There are a few ways to do this. A simple but not very efficient way is as follows:
h = {}
a = arr.uniq
#=> ["l", "g", "s", "c", "r", "i", "p", "u", "a", "o", "t", "n", "e"]
a.each { |c| h[c] = arr.count(c) }
h #=> {"l"=>1, "g"=>1, "s"=>1, "c"=>1, "r"=>1, "i"=>2, "p"=>2,
# "u"=>2, "a"=>2, "o"=>2, "t"=>2, "n"=>3, "e"=>3}
This would normally be written:
h = arr.uniq.each_with_object({}) { |c,h| h[c] = arr.count(c) }
The elements of h are in increasing order of value, but that's just coincidence. To ensure they are in that order (to make it easier to see the order), we would need to construct an array, sort it, then convert it to a hash:
a = arr.uniq.map { |c| [c, arr.count(c)] }
#=> [["l", 1], ["g", 1], ["s", 1], ["c", 1], ["r", 1], ["a", 2], ["p", 2],
# ["u", 2], ["i", 2], ["o", 2], ["t", 2], ["n", 3], ["e", 3]]
a = a.sort_by { |_,count| count }
#=> [["l", 1], ["g", 1], ["s", 1], ["c", 1], ["r", 1], ["a", 2], ["t", 2],
# ["u", 2], ["i", 2], ["o", 2], ["p", 2], ["n", 3], ["e", 3]]
h = Hash[a]
#=> {"l"=>1, "g"=>1, "s"=>1, "c"=>1, "r"=>1, "i"=>2, "t"=>2,
# "u"=>2, "a"=>2, "o"=>2, "p"=>2, "n"=>3, "e"=>3}
One would normally see this written:
h = Hash[arr.uniq.map { |c| [c, arr.count(c)] }.sort_by(&:last)]
or, in Ruby v2.0+:
h = arr.uniq.map { |c| [c, arr.count(c)] }.sort_by(&:last).to_h
Note that, prior to Ruby 1.9, there was no concept of key ordering in hashes.
The values of h's key-value pairs show that your sort is correct. It is not, however, very efficient. That's because in:
arr.sort_by { |x| arr.count(x) }
you repeatedly traverse arr, counting frequencies of elements. It's better to construct the hash above:
h = arr.uniq.each_with_object({}) { |c,h| h[c] = arr.count(c) }
before performing the sort, then:
arr.sort_by { |x| h[x] }
As an aside, let me mention a more efficient way to construct the hash h, one which requires only a single pass through arr:
h = Hash.new(0)
arr.each { |x| h[x] += 1 }
h #=> {"l"=>1, "g"=>1, "s"=>1, "c"=>1, "r"=>1, "a"=>2, "p"=>2,
# "u"=>2, "i"=>2, "o"=>2, "t"=>2, "n"=>3, "e"=>3}
or, more succinctly:
h = arr.each_with_object(Hash.new(0)) { |x,h| h[x] += 1 }
Here h is called a counting hash:
h = Hash.new(0)
creates an empty hash whose default value is zero. This means that if h does not have a key k, h[k] will return zero. The abbreviated assignment h[c] += 1 expands to:
h[c] = h[c] + 1
and if h does not have a key c, the default value is assigned to h[c] on the right side:
h[c] = 0 + 1 #=> 1
but the next time c is encountered:
h[c] = h[c] + 1
#=> 1 + 1 => 2
Lastly, let's start over and do as much as we can with String methods:
line = ("Ignore punctuation, please :)")
s = line.strip.downcase.gsub(/./) { |c| (c =~ /[a-z]/) ? c : '' }
#=> "ignorepunctuationplease"
h = s.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }
#=> {"i"=>2, "g"=>1, "n"=>3, "o"=>2, "r"=>1, "e"=>3, "p"=>2,
# "u"=>2, "c"=>1, "t"=>2, "a"=>2, "l"=>1, "s"=>1}
s.each_char.sort_by { |c| h[c] }
#=> ["l", "g", "s", "c", "r", "i", "p", "u", "a", "o", "t", "p",
# "a", "t", "i", "o", "u", "n", "n", "e", "e", "n", "e"]

How to join every X amount of characters together in an Array - Ruby

If I want to join every X amount of letters together in an array how could I implement this?
In this case I want to join every two letters together
Input: array = ["b", "i", "e", "t", "r", "o"]
Output: array = ["bi", "et", "ro"]
each_slice (docs):
arr = 'bietro'.split ''
# grab each slice of 2 elements
p arr.each_slice(2).to_a
#=> [["b", "i"], ["e", "t"], ["r", "o"]]
# map `join' over each of the slices
p arr.each_slice(2).map(&:join)
#=> ["bi", "et", "ro"]
#Doorknow shows the best way, but here are two (among many, many) other ways:
def bunch_em(arr,n)
((arr.size+n-1)/n).times.map { |i| arr.slice(i*n,n).join }
end
arr = ["b", "i", "e", "t", "r", "o"]
bunch_em(arr,2) #=> ["bi", "et", "ro"]

How do I create every combination of single elements selected from multiple arrays?

I have 5 arrays:
["A", "B", "C"]
["A", "B", "C", "D", "E"]
["A"]
["A", "B", "C", "D", "E", "F"]
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]
I would like to create a list of each combination as such:
["AAAAA","AAAAB","AAAAC", "AAAAD"...
"BAAAA","BAAAB","BAAAC", "BAAAD"...]
a = [
["A", "B", "C"],
["A", "B", "C", "D", "E"],
["A"],
["A", "B", "C", "D", "E", "F"],
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]
]
a.inject(&:product).map(&:join)
# => ["AAAAA", "AAAAB", "AAAAC", ..., "CEAFM", "CEAFN", "CEAFO"]
Thanks to bluexuemei for the improved answer. The original solution was a.shift.product(*a).map(&:join).
A More Traditional Solution
With such a convenient library, these ruby one-liners seem almost like cheating.
Here is a more traditional way to solve this common problem that can be readily coded into other programming languages:
N = a.reduce(1) { |product,list| product * list.size } # 1350
combinations = []
0.upto(N-1) do |q|
combo = []
a.reverse.each do |list|
q, r = q.divmod list.size
combo << list[r]
end
combinations.push combo.reverse.join
end
combinations
# => ["AAAAA", "AAAAB", "AAAAC", ..., "CEAFM", "CEAFN", "CEAFO"]
The basic idea is to first calculate the total number of combinations N which is just the product of the length of all the lists. Each integer from 0 to N-1 then encodes all the information needed to provide unique indices into each list to produce each combination. One way to think of it is that the index variable q can be expressed as a 5-digit number, where each digit is in a different base, where the base is the size of the corresponding list. That is, the first digit is base-3, the second digit is base-5, the 3rd is base-1 (always 0), the 4th is base-6, and the 5th is base-15. To extract these values from q, this is just taking a series of repeated integer divisions and remainders, as done in the inner loop. Naturally this requires some homework, perhaps looking at simpler examples, to fully digest.
a.reduce(&:product).map(&:join).size

Resources