Which way is better? Or do you know more better way?
data = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } } # 5 or more times
or
def object_in_hash(&object)
Hash.new { |h, k| h[k] = object.call() }
end
data = object_in_hash do object_in_hash do [] end end
You can create an infinitely nested hash by passing its default_proc to the new instance:
data = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
This lets you assign values to arbitrary keys:
data[:foo][:bar][:baz] = []
data[:foo][:bar][:qux] = []
data #=> {:foo=>{:bar=>{:baz=>[], :qux=>[]}}}
Encountered some code which sets an instance variable like so:
#square_array = Hash.new { Hash.new(false) }
Is there a reason to do it this way?
Rather then one of the following:
#square_array = Hash.new
# or
#square_array = {}
Yes, there is a reason. If you define hash with {} and try to get value of non-existing key, you get nil
hash = {}
#=> {}
hash[:a]
#=> nil
But if you define it with Hash.new you can set define value for non-existing key
hash = Hash.new { Hash.new(false) }
#=> {}
hash[:a]
#=> {}
hash[:a][:a]
#=> false
I have some simple_hash:
old_hash = {"New"=>"0"}
I wan to convert it to new format:
new_hash = old_hash.keys.each do |key|
hash = Hash.new
hash[key] = {count: old_hash[key]}
hash
end
but this code returns me:
["New"]
instead of:
{"New"=>{:count=>"0"}}
And the question is why?
You are confusing the syntax of block with that of a method. In your code, new_hash gets the value of old_hash.keys, which is not what you want.
A little modification works:
new_hash = Hash.new
old_hash.keys.each do |key|
new_hash[key] = {count: old_hash[key]}
end
Do this:
hash = Hash.new
new_hash = old_hash.keys.each do |key|
hash[key] = {count: old_hash[key]}
hash
end
hash
# => {"New"=>{:count=>"0"}}
Since you placed hash = Hash.new inside the loop, you are creating a new hash every time.
a beginner question here, please help me out
my_array = ["city1:state1","city2:state2","city3:state1","city4:state3","city5:state1"]
from this array how can i make a hash like this?
{
state1: [city1,city3,city5],
state2: [city2],
state3: [city4]
}
i am trying this way
my_hash = { }
my_array.each do |cs|
temp = cs.split(":")
if my_hash.keys.include? temp[1]
my_hash[temp[1]] = temp[0]
else
my_hash[temp[1]] = temp[0]
end
end
but i am not getting how to match keys of my hash and append to keys.
A little modification can work:
my_hash = { }
my_array.each do |cs|
temp = cs.split(":")
if my_hash.keys.include? temp[1].to_sym
my_hash[temp[1].to_sym] << temp[0]
else
my_hash[temp[1].to_sym] = [temp[0]]
end
end
Result is {:state1=>["city1", "city3", "city5"], :state2=>["city2"], :state3=>["city4"]}. I'm assuming this is what you mean (the keys are symbols, and the values are arrays of strings).
You can use Hash defaults to achieve an alternative solution:
my_array = ["city1:state1","city2:state2","city3:state1","city4:state3","city5:state1"]
hash = Hash.new do |hash, key|
hash[key] = []
end
my_array.each_with_object(hash) do |string, hash|
city, state = string.split(":")
hash[state.to_sym] << city
end
# => {:state1=>["city1", "city3", "city5"], :state2=>["city2"], :state3=>["city4"]}
Try this(considering that you mistyped state3:[city3] instead of state3:[city4] in your question):
my_array = ["city1:state1","city2:state2","city3:state1","city4:state3","city5:state1"]
my_hash = { }
my_array.each do |cs|
value, key = cs.split(":")
key = key.to_sym
if my_hash[key].present?
my_hash[key] << value
else
my_hash[key] = [value]
end
end
my_hash #=> {:state1=>["city1", "city3", "city5"], :state2=>["city2"], :state3=>["city4"]}
Or, one-liner:
my_hash = my_array.inject({}){|h, cs| value, key = cs.split(":"); key = key.to_sym; h[key].present? ? (h[key] << value) : h[key] = [value]; h }
my_hash #=> {:state1=>["city1", "city3", "city5"], :state2=>["city2"], :state3=>["city4"]}
or even better(based on jesper's idea of Hash):
my_array.inject(Hash.new{|h,k| h[k] = [] }){ |my_hash, cs| value, key = cs.split(":"); my_hash[key.to_sym] << value; my_hash }
my_hash #=> {:state1=>["city1", "city3", "city5"], :state2=>["city2"], :state3=>["city4"]}
I have a hash where the values are all arrays. I want to look up a key in this hash. If it exists I want to add a value to the array. If it does not exist (hash[key] returns nil) then I need to create the array and and add one value. Currently I have this:
hash[key].push elem unless hash[key].nul?
hash[key] ||= [elem]
This involves 3 lookups. I'm new to ruby so I'm sure there's a better way to do this. What is it?
My original plan was to make the default value for the hash [ ]. Then I can just use:
hash[key].push elem
Unfortunately if the key does not exist, that will only change the default value and not add a new key.
In this case you need to create a hash as below :
hash = Hash.new { |h,k| h[k] = [] }
The above is created to handle situations like your. Look new {|hash, key| block } → new_hash
hash = Hash.new { |h,k| h[k] = [] }
hash[:key1] << 1
hash[:key2] << 2
hash[:key1] << 3
hash # => {:key1=>[1, 3], :key2=>[2]}
You can try:
(hash[key] ||= []) << elem
However Arup's answer is much better.
You should create your hash with a default value.
hash = Hash.new { |h,k| h[k] = [] }
hash[key].push elem