2

I have the following array

a=[["kvm16", "one-415"], ["kvm16", "one-416"], ["kvm5", "one-417"]]

I would like to convert this to a hash that looks like this

{"kvm5"=>["one-417"], "kvm16"=>["one-417", "one-416"]}

everything I have tried squashes the value.

v=Hash[ a.collect do |p| [p[0], [ p[1] ] ] end ]
=> {"kvm5"=>["one-417"], "kvm16"=>["one-416"] }

I was thinking I could check to see if v[p[0]] is an array, but the variable isn't defined inside this block.

Is there a clean way to accomplish what I am looking for?

1
  • you mean {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}? Commented Jan 24, 2013 at 20:56

3 Answers 3

5

Yeah, you have to do it yourself, I'm afraid.

a = [["kvm16", "one-415"], ["kvm16", "one-416"], ["kvm5", "one-417"]]

h = a.each_with_object({}) do |(k, v), memo|
  (memo[k] ||= []) << v
end

h # => {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}

Or, if you're on older ruby (1.8.x):

h = {}
a.each do |k, v|
  (h[k] ||= []) << v
end

h # => {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}
Sign up to request clarification or add additional context in comments.

4 Comments

undefined method `each_with_object'
unfortunately I am unable to move to 1.9.3 in this instance. I am at ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux] Pasting the above code results in [["kvm16", "one-415"], ["kvm16", "one-416"], ["kvm5", "one-417"]]
a.each { |k,v| if h[k].is_a?(Array) ; h[k].push(v);else;h[k] = [v];end } => [["kvm16", "one-415"], ["kvm16", "one-416"], ["kvm5", "one-417"]] I must be missing something fundamental
Just copy/paste exactly, don't retype.
4

Let's see some functional approaches. This is a common abstraction, you can find it as Enumerable#map_by in Facets:

require 'facets'
hs.map_by { |k, v| [k, v] }
#=> {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}

This pattern replaces the cumbersome group_by + map + Hash:

Hash[hs.group_by(&:first).map { |k, gs| [k, gs.map(&:last)] }]
#=> {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}

Comments

0

this has it. in the face of not having each_with_object in my version of ruby and some googleze I arrived at

{}.tap{ |h| items.each{ |item| h[item.id] = item } }
=> {"kvm5"=>["one-417"], "kvm16"=>["one-415", "one-416"]}

mucho thanks to Sergio for getting me thinking along that line

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.