Add key-value for Ruby Hash from within the Hash - ruby

What's the best way to add a key-value pair to an hash object, from within the hash object itself?
The common way I know to add a new key to a hash is as follows:
hash = Hash.new
hash[:key] = 'value'
hash[:key] # => 'value'
What if I wan't to create a new hash which already has this key after its creation?
hash = Hash.new
hash[:key] # => 'value'
Is this possible? Thanks!

To create a Hash with an already initialized set of values you can do:
hash = { :key => 'value' }
hash[:key] # ===> This evaluates to 'value'
Just remember, the idiomatic way to create an empty hash in Ruby is:
hash = {}
Not hash = Hash.new like you exemplified.

Do you mean set the default value? is so, you could do with:
hash = Hash.new('value')
hash[:key] # => 'value'

Not sure what you mean i the other answers aren't what you want, you can create a hash with some keys and values allready filled like this
hash = {:key => 'value'} #{:key=>"value"}
and like the others said, the default value for key's not allready present is given by passing a block tot the hash at creation time like
hash = Hash.new('value') #{}
hash[:test] #"value"
or
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } #{}
h[:test] #"Go Fish: test"
Thje last sample teken from http://www.ruby-doc.org/core-1.9.3/Hash.html

Related

How to sort a hash within a hash?

I'm trying to sort a hash within a hash. I'd like to sort the hash by the sub-key. I'm using ruby.
I've tried using the sort_by method and iterating the over the hash to reorganize the sub-key. I received the error "ArgumentError: comparison of Hash with Hash failed"
hash = {2012 => {"regularseason" => [game_1, game_2, game_3],
"post_season" => [game_4, game_5]
},
2013 => {"regularseason" => [game_6, game_7, game_8],
"post_season" => [game_9, game_10]
},
2014 => {"post_season" => [game_11, game_12, game_13],
"regularseason" => [game_14, game_15]
}
}
Desired Result:
I would like to sort this hash so sub-key post_season will always appear before sub-key regularseason.
Use Hash#transform_values to sort values:
hash.transform_values { |v| v.sort.to_h }
#⇒ {2012=>{"post_season"=>[:game_4, :game_5],
# "regularseason"=>[:game_1, :game_2, :game_3]},
# 2013=>{"post_season"=>[:game_9, :game_10],
# "regularseason"=>[:game_6, :game_7, :game_8]},
# 2014=>{"post_season"=>[:game_11, :game_12, :game_13],
# "regularseason"=>[:game_14, :game_15]}}
Hashes return keys in the order they are inserted so I believe you'll essentially need to rewrite the nested hash.
For example:
hash.each { |(key, nested_hash)| hash[key] = nested_hash.sort.to_h }
This is rather inefficient, however, so you'd be better to see if you can ensure they are always entered in that order or somehow extract them in the order you desire.
Given a hash with keys that include k, we can return a new hash with the same key/value pairs, with k being the first key inserted, and the remaining keys maintaining their original relative order, as follows:
def reorder_key(h, key)
{ key=>h[key] }.merge h
end
For example:
h = { 1=>2, :a=>:b, "c"=>"d" }
reorder_key(h, :a)
#=> {:a=>:b, 1=>2, "c"=>"d"}
We can use this method to obtain the desired hash in the present problem.
hash.transform_values { |h| reorder_key(h, "post_season") }
#=> {2012=>{"post_season" =>[:game_4, :game_5],
# "regularseason"=>[:game_1, :game_2, :game_3]},
# 2013=>{"post_season" =>[:game_9, :game_10],
# "regularseason"=>[:game_6, :game_7, :game_8]},
# 2014=>{"post_season" =>[:game_11, :game_12, :game_13],
# "regularseason"=>[:game_14, :game_15]}}
This approach does not depend on "post_season" coincidentally preceding "regularseason" lexicographically. If, for example, it were decided to add the key "spring_training" and make that the first key to appear for each year in the returned hash, it would only be necessary to change the value of the second argument of reorder_key to "spring_training".

What is the difference between initializing hash two times and clear?

Suppose I have an hash = {1=>1,2=>2} to clear the values I can do either hash = {} or hash.clear . What is the difference between them?
According to the docs, if you see the source code for clear method, it iterates through each element in the hash and removes it.
When you do hash = {}, it will just create a new empty hash object but the old object will still be in memory ready for garbage collection if there are no other references for that object.
They are completely different.
The first one will simply bind the local variable hash to a different object. In particular, it will not "clear the values", as you claim:
hash = {i: 'am', still: 'there'}
another = hash
hash = {}
another
# => {i: 'am', still: 'there'}
The second one will actually clear the values:
hash = {i: 'am', still: 'there'}
another = hash
hash.clear
another
# => {}
Atri's answer is right, but beyond the Hash still existing in memory for GC when you use reassignment -- is if the array is still actually referenced by another variable.
A Hash is an object, a variable is just a pointer or reference to it. #clear clears the object itself, reassigning the variable just points the variable to a new Hash.
hash1 = {1=>1,2=>2}
hash2 = hash1
puts hash1 # => {1=>1,2=>2}
puts hash2 # => {1=>1,2=>2}
hash1 = {}
puts hash1 # => {}
puts hash2 # => {1=>1,2=>2}
hash1 = {1=>1,2=>2}
hash2 = hash1
hash1.clear
puts hash1 # => {}
puts hash2 # => {}

Why is the size of this Hash of arrays 0?

This is my code:
def mainFunction
#notes=Hash.new(Array.new)
#notes["First"].push(true)
#notes["First"].push(false)
#notes["First"].push(true)
#notes["Second"].push(true)
output #notes.size.to_s
end
Why is the output 0? It should be 2 since notes has two keys, "First" and "Second".
When initializing Hash values when accessed for the first time (ie. pushing onto key'd values that don't yet exist), you need to set the key on the Hash when it is requested.
#notes = Hash.new {|h, k| h[k] = []}
For reference, see the following result in the ruby repl initializing the Hash as you have
irb(main):063:0> #notes = Hash.new(Array.new)
=> {}
irb(main):064:0> #notes[:foo].push(true)
=> [true]
irb(main):065:0> #notes.has_key?(:foo)
=> false
irb(main):066:0> puts #notes.size
=> 0
and now the proper way.
irb(main):067:0> #notes = Hash.new {|h, k| h[k] = []}
=> {}
irb(main):068:0> #notes[:foo].push(true)
=> [true]
irb(main):069:0> #notes.has_key?(:foo)
=> true
irb(main):070:0> #notes.size
=> 1
The following statement:
#notes=Hash.new(Array.new)
Creates a hash with a default value: Array.new. When you access the hash with an unknown key, you will receive this default value.
Your other statements therefor change that default array:
#notes["First"].push(true)
This adds true to the default (empty) array.
You need to initialize the "First" array first:
#notes["First"] = []
And then you can add values:
#notes["First"].push(true)
Or more "rubyish":
#notes["First"] << true
The size now is:
#notes.size # => 1
#notes = Hash.new(Array.new) sets the default value for all elements to the same array. Since #notes contains no key "First", it returns that default value, and the .push adds to the end of it. But the key has not been added to the hash. The other pushes add to the end of the same default array.
Read this Hash.new(obj). It states:
If obj is specified, this single object will be used for all default
values.
Hence, when you do: #notes=Hash.new(Array.new). The default object will be an array.
#notes.default # => []
#notes['First'].push(true)
#notes.default # => [true]
#notes['First'].push(false)
#notes.default # => [true, false]
But, there won't be any entry as a key in #notes, of course you use access those values by giving any key(which may or may not exist), like this:
#notes['unknown_key'] #=> [true, false]
Hash.new(object) method call form just return object as a default value but does not update the hash.
You are looking for a block form where you can do the update:
#notes=Hash.new {|h, k| h[k] = []}

Ruby Hash.new weirdness [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Strange ruby behavior when using Hash.new([])
This is a simple one, as I'm lost for words.
Why is this happening:
1.9.3-p194 :001 > h = Hash.new([])
=> {}
1.9.3-p194 :002 > h[:key1] << "Ruby"
=> ["Ruby"]
1.9.3-p194 :003 > h
=> {}
1.9.3-p194 :004 > h.keys
=> []
1.9.3-p194 :005 > h[:key1]
=> ["Ruby"]
When you create a hash like this:
h = Hash.new([])
it means, whenever the hash is accessed with a key that has not been defined yet, its going to return:
[]
Now when you do :
h[:key1] << "Ruby"
h[:key1] has returned [] , to which "Ruby" got pushed, resulting in ["Ruby"], as output, as that is the last object returned. That has also got set as the default value to return when 'h' is accessed with an undefined key.
Hence, when you do :
h[:key1] or h[:key2] or h[:whatever]
You will get
"Ruby"
as output.
Hope this helps.
Look at the documentation of Hash.new
new → new_hash
new(obj) → new_hash
new {|hash, key| block } → new_hash
If this hash is subsequently accessed by a key that doesn’t correspond to a hash entry, the value returned depends on the style of new used to create the hash.
In the first form, the access returns nil.
If obj is specified, this single object will be used for all default values.
If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.
irb(main):015:0> h[:abc] # ["Ruby"]
So ["Ruby"] is returned as default value instead of nil if key is not found.
This construction Hash.new([]) returns default value but this value is not initialized value of hash. You're trying to work with hash assuming that the default value is a part of hash.
What you need is construction which will initialize the hash at some key:
hash = Hash.new { |h,k| h[k] = [] }
hash[:key1] << "Ruby"
hash #=> {:key1=>["Ruby"]}
You actually did not set the value with h[:keys] << "Ruby". You just add a value for the returned default array of a not found key. So no key is created.
If you write this, it will be okay:
h = Hash.new([])
h[:keys1] = []
h[:keys1] << "Ruby"
I have to admit this tripped me out too when I read your question. I had a look at the docs and it became clear though.
If obj is specified, this single object will be used for all default values.
So what you actually doing is modifying this one single array object that is used for the default values, without ever assigning to the key!
Check it out:
h = Hash.new([])
h[:x] << 'x'
# => ['x']
h
# => {}
h[:y]
# => ['x'] # CRAZY TIMES
So you need to do assignment somehow - h[:x] += ['x'] might be the way to go.

Iterate hash for specific range

How to pass range in hash to iterate from index 1 to last?
h = {}
h[1..-1].each_pair do |key,value|
puts "#{key} = #{value}
end
This code returning error. how may i pass range in hash ??
EDIT:
I want to print first key and value without any calculations.
From second key and value i want to do some calculation on my hash.
For that i have written this code ...
store_content_in_hash containing key and values.
first_key_value = store_content_in_hash.shift
f.puts first_key_value[1]
f.puts
store_content_in_hash.each_pair do |key,value|
store_content_in_hash[key].sort.each {|v| f.puts v }
f.puts
end
Any better way to solve out this problem ??
In Ruby 1.9 only:
Given a hash:
h = { :a => :b, :c => :d, :e => :f }
Go Like this:
Hash[Array(h)[1..-1]].each_pair do |key, value|
# ...
end
This will iterate through the following hash { :c => :d, :e => f } as the first key/value pair is excluded by the range.
Hashes have no concept of order. There is no such thing as the first or second element in a hash.
So you can't do what you want with hashes.
Hash is not about the ranges. It's about key value pairs. In ruby 1.8 hash is unordered hence you can't be sure in which order the keys and values will be iterated over which makes "range" thing obsolete. And I believe that you're doing something wrong (tm) in this situation. Can you elaborate on your problem?
On the other note you're getting an error because square brackets in Hash instance accepts keys. So if your hash does not contain 1..-1 as a key - you will get nil value and nil does not respond to each_pair. Try this to get a hold on this:
h = {(1..-1) => {:foo => :bar}}
h[1..-1].each_pair do |key,value|
puts "#{key} = #{value}"
end
As others have pointed out, Hashes are not about order. It's true that 1.9 hashes are ordered, but that's just a convenience, not their primary feature.
If the order is important to you, just use arrays instead of hashes. In your case, an array of pairs (two-element arrays) seems to fit the purpose. And if you need to access it by key, you can always easily convert it to a hash using Hash#[] method.

Resources