How to "zip" two arrays into hash - ruby

I want to "zip" two arrays into a Hash.
From:
['BO','BR']
['BOLIVIA','BRAZIL']
To:
{BO: 'BOLIVIA', BR:'BRAZIL'}
How can I do it?

I would do it this way:
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
Hash[keys.zip(values)]
# => {"BO"=>"BOLIVIA", "BR"=>"BRAZIL"}
If you want symbols for keys, then:
Hash[keys.map(&:to_sym).zip(values)]
# => {:BO=>"BOLIVIA", :BR=>"BRAZIL"}
In Ruby 2.1.0 or higher, you could write these as:
keys.zip(values).to_h
keys.map(&:to_sym).zip(values).to_h
As of Ruby 2.5 you can use .transform_keys:
Hash[keys.zip(values)].transform_keys { |k| k.to_sym }

Just use the single Array of the twos, and then transpose it, and generate Hash:
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
Hash[[keys,values].transpose]
# => {"BO"=>"BOLIVIA", "BR"=>"BRAZIL"}
or for newer ruby version:
[keys,values].transpose.to_h

Ironically, if you just sprinkle some dots and underscores into your question, it just works:
I want to "zip" two arrays into_hash
ary1.zip(ary2).to_h
# => { 'BO' => 'BOLIVIA', 'BR' => 'BRAZIL' }
Actually, you specified in your output hash that the keys should be Symbols not Strings, so we need to convert them first:
ary1.map(&:to_sym).zip(ary2).to_h
# => { BO: 'BOLIVIA', BR: 'BRAZIL' }

Quite readable version would be:
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
keys.zip(values).each_with_object({}) do |(key, value), hash|
hash[key.to_sym] = value
end

You can make a zipped array and then convert the array into hash like so :
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
array = key.zip(values) # => [['BO','BOLIVIA'],['BR','BRAZIL']]
hash = array.to_h # => {'BO' => 'BOLIVIA','BR' => 'BRAZIL'}

Related

How to access a symbol hash key using a variable in Ruby

I have an array of hashes to write a generic checker for, so I want to pass in the name of a key to be checked. The hash was defined with keys with symbols (colon prefixes). I can't figure out how to use the variable as a key properly. Even though the key exists in the hash, using the variable to access it results in nil.
In IRB I do this:
>> family = { 'husband' => "Homer", 'wife' => "Marge" }
=> {"husband"=>"Homer", "wife"=>"Marge"}
>> somevar = "husband"
=> "husband"
>> family[somevar]
=> "Homer"
>> another_family = { :husband => "Fred", :wife => "Wilma" }
=> {:husband=>"Fred", :wife=>"Wilma"}
>> another_family[somevar]
=> nil
>>
How do I access the hash key through a variable? Perhaps another way to ask is, how do I coerce the variable to a symbol?
You want to convert your string to a symbol first:
another_family[somevar.to_sym]
If you want to not have to worry about if your hash is symbol or string, simply convert it to symbolized keys
see: How do I convert a Ruby hash so that all of its keys are symbols?
You can use the Active Support gem to get access to the with_indifferent_access method:
require 'active_support/core_ext/hash/indifferent_access'
> hash = { somekey: 'somevalue' }.with_indifferent_access
=> {"somekey"=>"somevalue"}
> hash[:somekey]
=> "somevalue"
> hash['somekey']
=> "somevalue"
Since your keys are symbols, use symbols as keys.
> hash = { :husband => 'Homer', :wife => 'Marge' }
=> {:husband=>"Homer", :wife=>"Marge"}
> key_variable = :husband
=> :husband
> hash[key_variable]
=> "Homer"
If you use Rails with ActiveSupport, then do use HashWithIndifferentAccess for flexibility in accessing hash with either string or symbol.
family = HashWithIndifferentAccess.new({
'husband' => "Homer",
'wife' => "Marge"
})
somevar = "husband"
puts family[somevar]
#Homer
somevar = :husband
puts family[somevar]
#Homer
The things that you see as a variable-key in the hash are called Symbol is a structure in Ruby. They're primarily used either as hash keys or for referencing method names. They're immutable, and Only one copy of any symbol exists at a given time, so they save memory.
You can convert a string or symbol with .to_sym or a symbol to string with .to_s to illustrate this let me show this example:
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbolArray = [:HTML, :CSS, :JavaScript, :Python, :Ruby]
# Add your code below!
symbols = Array.new
strings.each {|x|
symbols.push(x.to_sym)
}
string = Array.new
symbolArray .each {|x|
string.push(x.to_s)
}
print symbols
print string
the result would be:
[:HTML, :CSS, :JavaScript, :Python, :Ruby]
["HTML", "CSS", "JavaScript", "Python", "Ruby"]
In ruby 9.1 you would see the symbols with the colons (:) in the right instead:
movies = { peter_pan: "magic dust", need_4_speed: "hey bro", back_to_the_future: "hey Doc!" }
I just wanted to make this point a litter more didactic so who ever is reading this can used.
One last thing, this is another way to solve your problem:
movie_ratings = {
:memento => 3,
:primer => 3.5,
:the_matrix => 3,
}
# Add your code below!
movie_ratings.each_key {|k|
puts k.to_s
}
result:
memento
primer
the_matrix

Clean way to search through hash keys in Ruby

I have a hash like so:
hash = {"jonathan" => "12", "bob" => 15 }
Then I have an array like so:
array = ['jon', 'bob']
I want to return the value of the key that includes a value in my array in the end.
The goal is something like this:
array.each do |name|
if hash.key.include? name
return hash.value
end
What's the best way to write that code block?
If you want multiple values, use Hash#values_at:
hash = {"jonathan" => "12", "bob" => 15 }
array = ['jon', 'bob']
hash.values_at(*array.select{|key| hash.has_key? key})
# => [15]
Using Array#&:
hash.values_at(*(hash.keys & array))
# => [15]
To get the keys, you don't need a block, this will do:
hash.keys & array
This takes the keys of the hash and intersect it with the array.
Second part, get a value from the hash:
hash[(hash.keys & array).last]
This will get the last key that is shared in hash and array and returns the value of that key in hash.
Also you can use select methods of Hash:
hash.select {| k,_ | array.include?( k ) }
# => {"bob"=>15}
and get, for example, last value:
hash.select {| k,_ | array.include?( k ) }.to_a.flatten.last
# => 15
or even use values_at method of Hash:
hash.values_at( *array ).compact.last
# => 15
Here is how I would write :
hash = {"jonathan" => "12", "bob" => 15 }
array = ['jon', 'bob']
array.collect(&hash.method(:[])).compact # => [15]

Ruby / Remove everything after a matched key / array of hashes

Let's say I have the following array of hashes:
h = [{"name" => "bob"}, {"car" => "toyota"}, {"age" => "25"}]
And I have the following key to match:
k = 'car'
How do I match the 'k' to 'h' and have delete every element after the match so that it returns:
h = [{"name" => "bob"}, {"car" => "toyota"}]
Just convert hash to array, do your task and then convert back
h = {"name" => "bob", "car" => "toyota", "age" => "25"}
array = h.to_a.flatten
index = array.index('car') + 1
h = Hash[*array[0..index]]
=> {"name"=>"bob", "car"=>"toyota"}
By the way, the hash is ordered only since Ruby 1.9
ar = [{"name" => "bob"}, {"car" => "toyota"}, {"age" => "25"}]
p ar[0 .. ar.index{|h| h.key?('car')}] #=>[{"name"=>"bob"}, {"car"=>"toyota"}]
I like megas' version, as its short and to the point. Another approach, which would be more explicit, would be iterating over the keys array of each hash. The keys of a hash are maintained in an ordered array (http://ruby-doc.org/core-1.9.3/Hash.html). They are ordered by when they were first entered. As a result, you can try the following:
newArray = Array.new
h.each do |hash| # Iterate through your array of hashes
newArray << hash
if hash.has_key?("car") # check if this hash is the "car" hash.
break # exits the block
end
end
This all depends, of course, on whether the array was created in the proper order. If it was, then you're golden.
A hash is unordered set by definition, so what you request is somewhat undefined. However you can do something like a hack:
h = {"name" => "bob", "car" => "toyota", "age" => "25"}
matched = false
key_given = "car"
h.each do |k,v|
if matched
h.delete(k)
end
if k == key_given
matched = true
next
end
end
I'm pretty late to the party here. I was looking for a solution to this same problem, but I didn't love these answers. So, here's my approach:
class Array
def take_until(&blk)
i = find_index &blk
take(i + 1)
end
end
h = [{"name" => "bob"}, {"car" => "toyota"}, {"age" => "25"}]
k = 'car'
h.take_until { |x| x.has_key?(k) }
=> [{"name"=>"bob"}, {"car"=>"toyota"}]

Remove keys in hash not in array

I can't find a way to remove keys from a hash that are not in a given array of key names. I read that I can use except or slice, but how can I feed them a list of the key names I want to keep? So for example, if I had this hash:
entry = {:title=>"casablanca", :media=>"dvd", :localeLanguage=>"en", :imdb=>"", :freebase=>"", :originalTitle => 'casablanca', :season=> '1', :episode => '3'}
and I only wanted to keep, say, :title, :media and :localeLanguage, how could I keep only those values whose key names I specify?
In Rails 4+, use slice:
entry = {:title=>"casablanca", :media=>"dvd", :localeLanguage=>"en", :imdb=>"", :freebase=>"", :originalTitle => 'casablanca', :season=> '1', :episode => '3'}
keepers = [:title, :media, :localeLanguage]
entry.slice(*keepers)
# => {:title=>"casablanca", :media=>"dvd", :localeLanguage=>"en"}
Shorter version (with same result):
entry.slice(*%i(title media localeLanguage))
Use slice! to modify your hash in-place.
I'd use keep_if (requires 1.9.2).
keepers = [:title, :media, :localeLanguage]
entry.keep_if {|k,_| keepers.include? k }
#=> {:title=>"casablanca", :media=>"dvd", :localeLanguage=>"en"}
In Ruby 1.9.3:
entry = entry.select do |key, value|
[:title, :media, :localeLanguage].include?(key)
end
p entry
# => {:title=>"casablanca", :media=>"dvd", :localeLanguage=>"en"}
In Ruby 1.8.7, Hash#select returns an array of arrays, so use Hash[] to turn that array into a hash:
entry = Hash[
entry.select do |key, value|
[:title, :media, :localeLanguage].include?(key)
end
]
# => {:media=>"dvd", :localeLanguage=>"en", :title=>"casablanca"}
The difference in order is because, in Ruby 1.8.7, Hashes are unordered.

hash assignment when (key => value) are stored in an array? (ruby)

I have hash (#post) of hashes where I want to keep the order of the hash's keys in the array (#post_csv_order) and also want to keep the relationship key => value in the array.
I don't know the final number of both #post hashes and key => value elements in the array.
I don't know how to assign the hash in a loop for all elements in the array. One by one #post_csv_order[0][0] => #post_csv_order[0][1] works nicely.
# require 'rubygems'
require 'pp'
#post = {}
forum_id = 123 #only sample values.... to make this sample script work
post_title = "Test post"
#post_csv_order = [
["ForumID" , forum_id],
["Post title", post_title]
]
if #post[forum_id] == nil
#post[forum_id] = {
#post_csv_order[0][0] => #post_csv_order[0][1],
#post_csv_order[1][0] => #post_csv_order[1][1]
##post_csv_order.map {|element| element[0] => element[1]}
##post_csv_order.each_index {|index| #post_csv_order[index][0] => #post_csv_order[index][1] }
}
end
pp #post
desired hash assignment should be like that
{123=>{"Post title"=>"Test post", "ForumID"=>123}}
The best way is to use to_h:
[ [:foo,1],[:bar,2],[:baz,3] ].to_h #=> {:foo => 1, :bar => 2, :baz => 3}
Note: This was introduced in Ruby 2.1.0. For older Ruby, you can use my backports gem and require 'backports/2.1.0/array/to_h', or else use Hash[]:
array = [[:foo,1],[:bar,2],[:baz,3]]
# then
Hash[ array ] #= > {:foo => 1, :bar => 2, :baz => 3}
This is available in Ruby 1.8.7 and later. If you are still using Ruby 1.8.6 you could require "backports/1.8.7/hash/constructor", but you might as well use the to_h backport.
I am not sure I fully understand your question but I guess you want to convert a 2d array in a hash.
So suppose you have an array such as:
array = [[:foo,1],[:bar,2],[:baz,3]]
You can build an hash with:
hash = array.inject({}) {|h,e| h[e[0]] = e[1]; h}
# => {:foo=>1, :bar=>2, :baz=>3}
And you can retrieve the keys in correct order with:
keys = array.inject([]) {|a,e| a << e[0] }
=> [:foo, :bar, :baz]
Is it what you were looking for ?
Answers summary
working code #1
#post[forum_id] = #post_csv_order.inject({}) {|h,e| h[e[0]] = e[1]; h}
working code #2
#post[forum_id] = Hash[*#post_csv_order.flatten]
working code #3
#post[forum_id] ||= Hash[ #post_csv_order ] #requires 'require "backports"'

Resources