How to Assign Hash['A']['B']= 'C' If Hash['A'] Doesn't Exist

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



Leave a reply



Submit