How to assign hash['a']['b']= 'c' if hash['a'] doesn't exist?
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
.
Working with Hashes that have a default value
0 will be the fallback if you try to access a key in the hash that doesn't exist
For example:
count = Hash.new
-> count['key'] => nil
vs
count = Hash.new(0)
-> count['key'] => 0
How to remove a key from Hash and get the remaining hash in Ruby/Rails?
Rails has an except/except! method that returns the hash with those keys removed. If you're already using Rails, there's no sense in creating your own version of this.
class Hash
# Returns a hash that includes everything but the given keys.
# hash = { a: true, b: false, c: nil}
# hash.except(:c) # => { a: true, b: false}
# hash # => { a: true, b: false, c: nil}
#
# This is useful for limiting a set of parameters to everything but a few known toggles:
# @person.update(params[:person].except(:admin))
def except(*keys)
dup.except!(*keys)
end
# Replaces the hash without the given keys.
# hash = { a: true, b: false, c: nil}
# hash.except!(:c) # => { a: true, b: false}
# hash # => { a: true, b: false }
def except!(*keys)
keys.each { |key| delete(key) }
self
end
end
Is auto-initialization of multi-dimensional hash array possible in Ruby, as it is in PHP?
Try this:
def hash_with_default_hash
Hash.new { |hash, key| hash[key] = hash_with_default_hash }
end
a = hash_with_default_hash
If the key doesn't exist, then the result of the block will be used as the default value. In this case, the default value is also a hash which uses hashes as its default values.
How can I add new keys to a dictionary?
You create a new key/value pair on a dictionary by assigning a value to that key
d = {'key': 'value'}
print(d) # {'key': 'value'}
d['mynewkey'] = 'mynewvalue'
print(d) # {'key': 'value', 'mynewkey': 'mynewvalue'}
If the key doesn't exist, it's added and points to that value. If it exists, the current value it points to is overwritten.
See if a key in one hash does not exist in another hash?
a = { :a => 1, :b => 2 , :v => 3}
b = { :a => 3, :b => 22 , :g => 3}
a.keys - b.keys
#=> [:v]
b.keys - a.keys
#=> [:g]
Ruby Hash.new with a block need in-depth explanation
arr = [1, 2, 1, 3, 2, 1]
You could write your code with no bells or whistles
def duped_index(arr)
result = {}
arr.each_with_index do |ele, idx|
result[ele] = [] unless result.key?(ele)
result[ele] << idx
end
result.select { |ele, indices| indices.length > 1 }
end
duped_index(arr)
#=> {1=>[0, 2, 5], 2=>[1, 4]}
Another way is create empty arrays on the fly, as needed
def duped_index(arr)
result = {}
arr.each_with_index { |ele, idx| (result[ele] ||= []) << idx }
result.select { |ele, indices| indices.length > 1 }
end
duped_index(arr)
#=> {1=>[0, 2, 5], 2=>[1, 4]}
Ruby's parser expands the abbreviated assignment result[ele] ||= []
to:
result[ele] = result[ele] || = []
If result
does not have a key ele
, result[ele] #=> nil
, so
result[ele] = nil || = []
#=> []
If result
has a key ele
result[ele]
remains unchanged. Therefore,
(result[ele] ||= []) << idx
causes idx
to be appended to the array (empty or otherwise) that is the value of result
for the key ele
.
This method would more commonly be written as follows:
def duped_index(arr)
arr.each_with_index.with_object({}) { |(ele, idx), result|
(result[ele] ||= []) << idx }.
select { |ele, indices| indices.length > 1 }
end
A third way is to create a hash with a default proc, as in the question
Suppose:
result = Hash.new { |hash, key| hash[key] = [] }
#=> {}
Now perform the following operation:
result['dog'] << 'woof'
#=> ["woof"]
result
#=> {"dog"=>["woof"]}
When result['dog']
is executed Ruby sees that result.key? #=> false
, so she executes the block, first by assigning values to the block variables:
hash, key = [result, 'dog']
#=> [{}, 'dog']
hash
#=> {}
key
#=> 'dog'
Then executes:
hash['key'] = []
resulting in:
result
#=> { 'dog'=>[] }
She then executes:
result['dog'] << 'woof'
result
#=> {"dog"=>["woof"]}
Now suppose we execute:
result['dog'] << 'I love kibble!'
result
#=> {"dog"=>["woof", "I love kibble!"]}
This time Ruby sees that result
has a key 'dog'
, so she simply appends "I love kibble!"
to the array result['dog']
, without referencing the block.
Let's take another example:
result = Hash.new do |hash, key|
puts "I just launched the missiles...just kidding"
hash[key] = []
end
result['dog'] << 'woof'
I just launched the missiles...just kidding
#=> ["woof"]
The behaviour is the same as before except a message is displayed as well. The point is that you can put any code you like in the block, extracting data from database being an example (though I don't think that's a common use of default procs).
The method using this form of Hash#new would commonly be written:
def duped_index(arr)
arr.each_with_index.
with_object(Hash.new { |h,k| h[k]=[] }) { |(ele,idx), result|
result[ele] << idx }.
select { |ele, indices| indices.length > 1 }
end
The choice of which approach to take is mainly a matter of taste, but I expect most Rubyists would elect #2 or #3.
Related Topics
Are Crlf Lines Ok in a Rails Project Deployed on Linux
How to Use the Debugger with Ruby 2.0
Extract Single String from HTML Using Ruby/Mechanize (And Nokogiri)
Ruby: How to Turn a Hash into Http Parameters
Uninstalling All Gems Ruby 2.0.0
How to Save a Base64 String as an Image Using Ruby
Rails: Is There a Rails Trick to Adding Commas to Large Numbers
Dynamic Class Definition with a Class Name
Class ≪≪ Self VS Self.Method With Ruby: What's Better
Google Plus API Shutdown Today, Which Alternative Can Be Used to Authentication
Popen Getting Pid of Newly Run Process
Ruby Dependency Injection Libraries
Rails Dot Instead of Slash in Url
Create a Devise User from Ruby Console
Correctly Doing Redirect_To :Back in Ruby on Rails When Referrer Is Not Available