开发者

Creating a new hash with default keys

I want to create a hash with an index that comes from an array.

ary = ["a", "b", "c"]
h = Hash.new(ary.each{|a| h[a] = 0})

My goal is to start with a hash like this:

h = {"a"=>0, "b"=>0, "c"=>0}

so that later 开发者_C百科when the hash has changed I can reset it with h.default

Unfortunately the way I'm setting up the hash is not working... any ideas?


You should instantiate your hash h first, and then fill it with the contents of the array:

h = {}    
ary = ["a", "b", "c"]
ary.each{|a| h[a] = 0}


Use the default value feature for the hash

h = Hash.new(0)

h["a"]      # => 0

In this approach, the key is not set.

h.key?("a") # => false

Other approach is to set the missing key when accessed.

h = Hash.new {|h, k| h[k] = 0}
h["a"]      # => 0
h.key?("a") # => true

Even in this approach, the operations like key? will fail if you haven't accessed the key before.

h.key?("b") # => false
h["b"]      # => 0
h.key?("b") # => true

You can always resort to brute force, which has the least boundary conditions.

h = Hash.new.tap {|h| ["a", "b", "c"].each{|k| h[k] = 0}}
h.key?("b") # => true
h["b"]      # => 0


You can do it like this where you expand a list into zero-initialized values:

list = %w[ a b c ]

hash = Hash[list.collect { |i| [ i, 0 ] }]

You can also make a Hash that simply has a default value of 0 for any given key:

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

Any new key referenced will be pre-initialized to the default value and this will avoid having to initialize the whole hash.


This may not be the most efficient way, but I always appreciate one-liners that reveal a little more about Ruby's versatility:

h =  Hash[['a', 'b', 'c'].collect { |v|  [v, 0] }]

Or another one-liner that does the same thing:

h = ['a', 'b', 'c'].inject({}) {|h, v| h[v] = 0; h }

By the way, from a performance standpoint, the one-liners run about 80% of the speed of:

h = {}
ary = ['a','b','c']
ary.each { |a| h[a]=0 }


Rails 6 added index_with on Enumerable module. This will help in creating a hash from an enumerator with default or fetched values.

ary = %w[a b c]
hash = ary.index_with(0) # => {"a"=>0, "b"=>0, "c"=>0}


Another option is to use the Enum#inject method which I'm a fan of for its cleanliness. I haven't benchmarked it compared to the other options though.

h = ary.inject({}) {|hash, key| hash[key] = 0; hash}


Alternate way of having a hash with the keys actually added

Hash[[:a, :b, :c].zip([])] # => {:a=>nil, :b=>nil, :c=>nil}

Hash[[:a, :b, :c].zip(Array.new(3, 0))] # => {:a=>0, :b=>0, :c=>0}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜