0

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!

1
  • 1
    Note: I have tried replacing nil with [ ] in Hash[*tags.map {|k| [k, nil]}.flatten], that doesn't work Commented Jun 7, 2015 at 16:44

4 Answers 4

3

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"=>[]}
Sign up to request clarification or add additional context in comments.

1 Comment

You can also write Hash[*tags.flat_map { |k| [k, []]}].
1

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.

4 Comments

Nice. Very nice! (I noticed the mindmeld. btw, who's "Rachel"?)
I feel funny pasting a comment, but since this is the same solution...problem is the same Array object gets assigned to all three values, which is probably undesirable.
David, I was going to say "This is completely wrong. What were you thinking??", but realized that wouldn't make much sense now that I've deleted my identical answer, so I won't say that. @Kristján, you were right in doing so. Good catch.
I'm sort of disappointed I noticed, it was a really slick solution otherwise. Now I'm going to be looking for ways to use product all week :)
0

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

Comments

0

One more way:

tags = %w|tag1 tag2 tag3|

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.