0

I have the following array and I want to convert it a hash with keys as age and values like the name of the person. Additionally, I want to make sure that folks of the same age belong to the same key.

ages = [ ['alpha', 20], ['beta',21], ['charlie',23], ['delta', 20] , ['gamma', 23]]

how do I convert the above into a hash like below?

eg - {20 => ['alpha','delta'] } etc.

i have tried the following code but i am getting stuck beyond this point:

hash = Hash[ages.collect {|item| [item[1], item[0]]} ]

pp hash

thank you.

4 Answers 4

2

One of the possible solutions is to use:

hash = ages.each_with_object(Hash.new {|h,k| h[k] = [] }) do |obj, res|
  res[obj.last] << obj.first
end

=> {20=>["alpha", "delta"], 21=>["beta"], 23=>["charlie", "gamma"]}
  1. Hash#default_proc
  2. Enumerable#each_with_object
Sign up to request clarification or add additional context in comments.

Comments

2

Yet another way to do this: Hash#transform_values with Enumerable#group_by

ages.
  group_by(&:second).
  transform_values { |entries| entries.map(&:first) }
# => {20=>["alpha", "delta"], 21=>["beta"], 23=>["charlie", "gamma"]}

4 Comments

&:second? Was there a rails tag previously?
Hah I can't remember what's in rails vs ruby to be honest. And this answer also relies on active support methods as I said.
group_by is ruby Enumerable not ActiveSupport
thanks ... like i said, i can't keep track, but that's good to know.
1

you can create the hash first and loop through the ages collecting members of similar ages into arrays.

hash = Hash.new{|h,k|h[k]=Array.new()}
ages.each do |age|
  hash[age.last]<<age.first
end
p hash #{20=>["alpha", "delta"], 21=>["beta"], 23=>["charlie", "gamma"]}

Comments

1
ages.each_with_object({}) do |(str,x),h|
  (h[x] ||= []) << str
end
  #=> {20=>["alpha", "delta"], 21=>["beta"], 23=>["charlie", "gamma"]}

h[x] ||= [] expands to

h[x] = h[x] || []

If h does not have a key x this becomes

h[x] = nil || []

causing h[x] to be set equal to an empty array, after which h[x] << str is executed.

Expressing the block variables as |(str,x),h| makes use of Ruby's array decomposition:

enum = ages.each_with_object({})
  #=> #<Enumerator: [["alpha", 20], ["beta", 21], ["charlie", 23],
  #     ["delta", 20], ["gamma", 23]]:each_with_object({})> 
(str,x),h = enum.next
  #=> [["alpha", 20], {}] 
str
  #=> "alpha" 
x #=> 20 
h #=> {} 
(h[x] ||= []) << str
  #=> ["alpha"] 

(str,x),h = enum.next
  #=> [["beta", 21], {20=>["alpha"]}] 

and so on.

1 Comment

How about h[x]= [*h[x],str] instead :). I know it makes a bunch of discarded Arrays it just looks so fun

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.