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.
Related
Example as follows:
if house['windows'][floor_1]
house['windows'][floor_1] << north_side
else
house['windows'][floor_1] = [north_side]
end
Best way to check for existing key?
The fact that house['windows'] is an element in a hash already is a bit of a red herring, so I will use windows as a variable referencing a hash.
Set up a default value for the windows hash, so that any non-preexisting key is assigned an array value:
windows = Hash.new {|hash, key| hash[key] = [] }
Now you can append (<<) to new hash elements automatically.
windows['floor_1'] << 'north_side'
windows # => {"floor_1"=>["north_side"]}
For your specific case, replace windows with house['windows'].
EDIT
As pointed out in the comments, this behavior can be added to an already-instantiated hash:
windows.default_proc = proc {|hash, key| hash[key] = [] }
I would do something like:
house['windows'][floor_1] ||= []
house['windows'][floor_1] << north_side
Given your Hash, I imagine:
house = { windows: { floor_0: ['f0'] } }
You can check the existence of a key using Hash#has_key?
house[:windows].has_key? :floor_1 #=> false
So you can create it:
house[:windows].merge!({floor_1: []}) unless house[:windows].has_key? :floor_1
Better if you define a defalt value using for example Hash#default_proc=:
house[:windows].default_proc = proc { |h, k| h[k] = [] }
So you can
house[:windows][:floor_3] << 'f3'
house #=> {:windows=>{:floor_0=>["f0"], :floor_1=>[], :floor_3=>["f3"]}}
Is there a more elegant way to write this code?
def create_a_hash_from_a_collection
my_hash = {}
collection_of_hashes.each do |key, value|
my_hash[key] = {} if my_hash[key].nil?
my_hash[key] = value
end
my_hash
end
The line that seems clumsy to me is this:
my_hash[key] = {} if my_hash[key].nil?
Is there a shorthand way of expressing it?
If you want to have a brand new hash for each key in your initial hash you need to initialise it with a block:
hash = Hash.new { |hash, key| hash[key] = {} }
hash[:foo].object_id == hash[:bar].object_id #=> false
Otherwise, if you do this, It will be always the same default hash
hash = Hash.new({})
hash[:foo].object_id == hash[:bar].object_id #=> true
You can use ||= operator which does exactly what you want
my_hash[key] ||= {}
The rest of my answer here is because I'm not sure what a "collection of hashes" is, so my best guess is that it would be an array of hashes. If I'm wrong, let me know and disregard the rest of this answer.
It seems that the rest of your method may not do what it sounds like you're trying to do. Consider:
#collection_of_hashes = [{foo: 'bar'}, {baz: 'qux'}]
def create_a_hash_from_a_collection
my_hash = {}
#collection_of_hashes.each do |key, value|
# this is not actually doing anything here and returns same with or
# without the following line
# my_hash[key] ||= {}
my_hash[key] = value
end
my_hash
end
#=> {{:foo=>"bar"}=>nil, {:baz=>"qux"}=>nil}
But what you probably want is
def create_a_hash_from_a_collection
my_hash = {}
#collection_of_hashes.each do |hash|
hash.keys.each do |k|
my_hash[k] = hash[k]
end
end
my_hash
end
#=> {:foo=>"bar", :baz=>"qux"}
But also keep in mind, if any of your "collection of hashes" which we would tend to assume would be an array of hashes, contain the same key, which one wins? This code, it would be the last item in the array's key value. What is the actual goal of your method?
I guess what you want is to initialise your my_hash with a default value, so then you don't need to check if it's nil or not.
That can be done using the Hash.new constructor, compare:
my_hash = {}
puts my_hash['no_existing_key'] #=> nil
my_hash = Hash.new({})
puts my_hash['no_existing_key'] #=> {}
You then can reduce your code to:
def create_a_hash_from_a_collection
my_hash = Hash.new({})
collection_of_hashes.each do |key, value|
my_hash[key] = value
end
my_hash
end
Since you are assigning value anyway, maybe you could use my_hash[key] = value || {}?
So, if value to assign is nil, the value of that key becomes {}.
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
I learned from this answer here that this is possible:
h = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
h['bar'] # => {}
h['tar']['star']['par'] # => {}
Can someone explain how it works?
Hashes have a thing called a default_proc, which is simply a proc that Ruby runs when you try to access a hash key that doesn't exist. This proc receives both the hash itself and the target key as parameters.
You can set a Hash's default_proc at any time. Passing a block parameter to Hash.new simply allows you to initialize a Hash and set its default_proc in one step:
h = Hash.new
h.default_proc = proc{ |hash, key| hash[key] = 'foo' }
# The above is equivalent to:
h = Hash.new{ |hash, key| hash[key] = 'foo' }
We can also access the default proc for a hash by calling h.default_proc. Knowing this, and knowing that the ampersand (&) allows a proc passed as a normal parameter to be treated as a block parameter, we can now explain how this code works:
cool_hash = Hash.new{ |h, k| h[k] = Hash.new(&h.default_proc) }
The block passed to Hash.new will be called when we try to access a key that doesn't exist. This block will receive the hash itself as h, and the key we tried to access as k. We respond by setting h[k] (that is, the value of the key we're trying to access) to a new hash. Into the constructor of this new hash, we pass the "parent" hash's default_proc, using an ampersand to force it to be interpreted as a block parameter. This is the equivalent of doing the following, to an infinite depth:
cool_hash = Hash.new{ |h, k| h[k] = Hash.new{ |h, k| h[k] = Hash.new{ ... } } }
The end result is that the key we tried to access was initialized to a new Hash, which itself will initialize any "not found" keys to a new Hash, which itself will have the same behavior, etc. It's hashes all the way down.
In this code you create hashes by chain, so that any link of chain would have same default_proc
So, default_proc of h and h['bar'] and so far will be the same - it will return new instance of Hash with this default_proc
Is there any way simpler than
if hash.key?('a')
hash['a']['b'] = 'c'
else
hash['a'] = {}
hash['a']['b'] = 'c'
end
The easiest way is to construct your Hash with a block argument:
hash = Hash.new { |h, k| h[k] = { } }
hash['a']['b'] = 1
hash['a']['c'] = 1
hash['b']['c'] = 1
puts hash.inspect
# "{"a"=>{"b"=>1, "c"=>1}, "b"=>{"c"=>1}}"
This form for new creates a new empty Hash as the default value. You don't want this:
hash = Hash.new({ })
as that will use the exact same hash for all default entries.
Also, as Phrogz notes, you can make the auto-vivified hashes auto-vivify using default_proc:
hash = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
UPDATE: I think I should clarify my warning against Hash.new({ }). When you say this:
h = Hash.new({ })
That's pretty much like saying this:
h = Hash.new
h.default = { }
And then, when you access h to assign something as h[:k][:m] = y, it behaves as though you did this:
if(h.has_key?(:k))
h[:k][:m] = y
else
h.default[:m] = y
end
And then, if you h[:k2][:n] = z, you'll end up assigning h.default[:n] = z. Note that h still says that h.has_key?(:k) is false.
However, when you say this:
h = Hash.new(0)
Everything will work out okay because you will never modified h[k] in place here, you'll only read a value from h (which will use the default if necessary) or assign a new value to h.
a simple one, but hash should be a valid hash object
(hash["a"] ||= {})['b'] = "c"
If you create hash as the following, with default value of a new (identically default-valued) hash: (thanks to Phrogz for the correction; I had the syntax wrong)
hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
Then you can do
hash["a"]["b"] = "c"
without any additional code.
The question here:
Is auto-initialization of multi-dimensional hash array possible in Ruby, as it is in PHP?
provides a very useful AutoHash implementation that does this.
class NilClass
def [](other)
nil
end
end
Once you defined that, everything will work automatically. But be aware that from now on nil would behave as an empty hash when used as a hash.