14

How to initialize a hash with keys from an array like the following?

keys = [ 'a' , 'b' , 'c' ]

Desired hash h should be:

puts h 
# { 'a' => nil , 'b' => nil , 'c' => nil }
1

6 Answers 6

16

Here we go using Enumerable#each_with_object and Hash::[].

 keys = [ 'a' , 'b' , 'c' ]
 Hash[keys.each_with_object(nil).to_a]
 # => {"a"=>nil, "b"=>nil, "c"=>nil}

or Using Array#product

keys = [ 'a' , 'b' , 'c' ]
Hash[keys.product([nil])]
# => {"a"=>nil, "b"=>nil, "c"=>nil}
Sign up to request clarification or add additional context in comments.

5 Comments

Getting the following error - '>> attr = Hash[keys.each_with_object(nil)] ArgumentError: odd number of arguments for Hash from (irb):29:in []' from (irb):29 from C:/RailsInstaller/Ruby1.9.3/bin/irb:12:in <main>' >> '
@user3206440 try Hash[keys.each_with_object(nil).to_a].
That method is Hash.[], not Kernel#Hash, they are quite different methods.
How about initializing the key values to empty arrays? Something like the following puts h # { 'a' => [] , 'b' => [] , 'c' => [] }
Hash[ keys.product([ [] ]) ] Will not do what you think! It initializes each member of the hash to the same Array value. Adding a member to one of the arrays adds the same member to all the arrays.
10

Using the new (Ruby 2.1) to_h:

keys.each_with_object(nil).to_h

2 Comments

myself still in Ruby2.0.0 ... Shame on me :)
This ideas seems brilliant. Can you explain a little bit more how does each_with_object works together with to_h? I try to look on the documentation but cannot figure it out well.
6

Another alternative using Array#zip:

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

Update: As suggested by Arup Rakshit here's a performance comparison between the proposed solutions:

require 'fruity'

ary = *(1..10_000)

compare do
  each_with_object {  ary.each_with_object(nil).to_a  }
  product          {  ary.product([nil])  }
  zip              {  ary.zip([])  }
  map              {  ary.map { |k| [k, nil] }  }
end

The result:

Running each test once. Test will take about 1 second.
zip is faster than product by 19.999999999999996% ± 1.0%
product is faster than each_with_object by 30.000000000000004% ± 1.0%
each_with_object is similar to map

6 Comments

create a benchmark report.. You are best on it. Lets see which one is fast.. :)
Can you explain please?
@ArupRakshit zip is faster, but for sure each_with_index is more clear and flexible (using zip you can't produce an hash like {'a' => [], 'b' => [], .... My choice would be using product which is the best compromise, IMHO.
it is not each_with_index, rather each_with_object.. :)
@Marc-AndréLafortune btw, I was just checking your code for compare_block and NamedBlockCollector, and it's one of the most beautiful things I've seen in Ruby!
|
1
=> keys = [ 'a' , 'b' , 'c' ]
=> Hash[keys.map { |x, z| [x, z] }]
# {"a"=>nil, "b"=>nil, "c"=>nil}

=> Hash[keys.map { |x| [x, nil] }]
# {"a"=>nil, "b"=>nil, "c"=>nil}

=> Hash[keys.map { |x, _| [x, _] }]
# {"a"=>nil, "b"=>nil, "c"=>nil}

2 Comments

Why not just keys.map { |k| [k, nil] }?
or keys.map { |k, _| [k, _] }
0

I think you should try:

x = {}
keys.map { |k, _| x[k.to_s] = _ }

1 Comment

Don't need keys.map { |k, _| x[k.to_s] = _ }. Rather keys.each { |k, _| x[k.to_s] = _ } is enough.
0

If you need to initialize the hash with something specific, you could also use Array#zip with Array#cycle:

Hash[keys.zip([''].cycle)]
# => {"a"=>"", "b"=>"", "c"=>""}

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.