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
Ruby Symbols VS Strings in Hashes
Error Occurred While Installing Mini_Racer (0.2.0)
Why I Can Not Call Super in Define_Method with Overloading Method
Are Ruby 1.9 Regular Expressions Equally Powerful to a Context Free Grammar
"Errno::Eaccess...Permission Denied" Running Compass Watch
Model Using Modules in Rails Application
Convert String to Class Name Without Using Eval in Ruby
How to Get My Aws Lambda to Access Gems Stored in Vendor/Bundle
How to Use a Branch in a Fork of Rails in a Project with Bundler
Parallel Http Requests in Ruby
Simple Ruby Input Validation Library
Sinatra Static Assets Are Not Found When Using Rackup
How to Tokenize This String in Ruby
How to Prevent Pipe Character from Causing a Bad Uri Error in Rails 3/Ruby 1.9.2
Can't Enter Umlauts in Ruby 1.9.3 Irb
Ruby String.Encode Still Gives "Invalid Byte Sequence in Utf-8"