Creating a Hash with Values as Arrays and Default Value as Empty Array

Creating a Hash with values as arrays and default value as empty array

Lakshmi is right. When you created the Hash using Hash.new([]), you created one array object.

Hence, the same array is returned for every missing key in the Hash.

That is why, if the shared array is edited, the change is reflected across all the missing keys.

Using:

Hash.new { |h, k| h[k] = [] }

Creates and assigns a new array for each missing key in the Hash, so that it is a unique object.

Initializing a Hash with empty array unexpected behaviour

Just do

a = Hash.new { |h, k| h[k] = [] }
a[1] << "asd"
a # => {1=>["asd"]}

Read the below lines from the Hash::new documentation. It really explains why you didn't get the desired result.

new(obj) → new_hash

If obj is specified, this single object will be used for all default values.

new {|hash, key| block } → new_hash

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.

You can test by hand :

a = Hash.new([])
a[1].object_id # => 2160424560
a[2].object_id # => 2160424560

Now with the above style of Hash object creation, you can see every access to an unknown key, returning back the same default object. Now the other way, I meant block way :

b = Hash.new { |h, k| [] }
b[2].object_id # => 2168989980
b[1].object_id # => 2168933180

So, with the block form, every unknown key access, returning a new Array object.

Creating a Hash with keys from an array and empty arrays as the values

Using flatten, map [] instead of nil like you tried, then use flatten(1). That eliminates only the first layer of array, so you get ['tag1', [], ...] to pass to Hash[].

> tags = %w[tag1 tag2 tag3]
=> ["tag1", "tag2", "tag3"]
> tags.map {|k| [k, []]}.flatten
=> ["tag1", "tag2", "tag3"]
> tags.map {|k| [k, []]}.flatten(1)
=> ["tag1", [], "tag2", [], "tag3", []]
> Hash[*tags.map {|k| [k, []]}.flatten(1)]
=> {"tag1"=>[], "tag2"=>[], "tag3"=>[]}

You can also avoid flatten altogether if you drop the splat (*) from Hash[], since ::[] also accepts a list of pairs.

> tags.map {|k| [k, []]}
=> [["tag1", []], ["tag2", []], ["tag3", []]]
> Hash[tags.map {|k| [k, []]}]
=> {"tag1"=>[], "tag2"=>[], "tag3"=>[]}

How to convert an array to a hash with specified common values

There are many ways, e.g.:

arr.zip([{}] * arr.size).to_h

or (the latter, thx @Stefan, is not probably what you wanted, since it will share one hash for all keys):

arr.product([{}]).to_h

Populate hash with array keys and default value

Do as below :

def solution(keys,default_val)
Hash[keys.product([default_val])]
end

solution([:key1,:key2],12) # => {:key1=>12, :key2=>12}

Read Array#product and Kernel#Hash.



Related Topics



Leave a reply



Submit