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

805 Views Asked by At

I have the array tags, which consists of a number of Strings that I need to use to create a new Hash (content) where each value is an empty array. I currently have Hash[*tags.map {|k| [k, nil]}.flatten], but this returns:

{
  "tag1" => nil,
  "tag2" => nil,
  "tag3" => nil
}

when I want it to be

{
  "tag1" => [],
  "tag2" => [],
  "tag3" => []
}

sorry this is kind of a dumb question, but I've googled around and can't find the answer. Thank you!

4

There are 4 best solutions below

1
On BEST ANSWER

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"=>[]}
0
On

One more way:

tags = %w|tag1 tag2 tag3|

Hash.new { |h,k| h[k] = [] }.tap { |h| h.values_at(*tags) }
  #=> {"tag1"=>[], "tag2"=>[], "tag3"=>[]} 
0
On

According to the docs Hash.new accepts a block which you can use to provide a default value each time a new key, not belonging to the Hash, is accessed. So you can use this

tags = %w(tag1 tag2 tag3)
h = Hash.new{ |hash, key| hash[key] = [] }
tags.each{ |tag| h[tag] }
# h == {"tag1"=>[], "tag2"=>[], "tag3"=>[]}
h['tag4']
# h == {"tag1"=>[], "tag2"=>[], "tag3"=>[], "tag4"=>[]}
4
On

Ruby's Array supports (Cartesian) product so you can take advantage of this, without need of extra logic built inside block(s):

> tags.product([[]]).to_h
=> {"tag1"=>[], "tag2"=>[], "tag3"=>[]}

Simple.