ben
ben

Reputation: 29817

How do I convert an array of hashes into a sorted hash?

If I have an array of hashes, each with a day key:

[
    {:day=>4,:name=>'Jay'},
    {:day=>1,:name=>'Ben'},
    {:day=>4,:name=>'Jill'}
]

What is the best way to convert it to a hash with sorted day values as the keys:

{
    :1=>[{:day=>1,:name=>'Ben'}],
    :4=>[{:day=>4,:name=>'Jay'},{:day=>4,:name=>'Jill'}]
}

I'm using Ruby 1.9.2 and Rails 3.1.1

Upvotes: 0

Views: 1051

Answers (4)

Anurag
Anurag

Reputation: 141909

Assuming you array is called is list, here's one way using the reduce method:

list.reduce({}) { |hash, item|
  (hash[item[:day]] ||= []) << item; hash
}

Here's another using the map method, but you have to carry a holder variable around:

hash = {}
list.each { |item|
  (hash[item[:day]] ||= []) << item
}

Once you have the unsorted hash say in variable foo, you can sort it as,

Hash[foo.sort]

Upvotes: 1

Victor Moroz
Victor Moroz

Reputation: 9225

In Rails you can use OrderedHash:

ActiveSupport::OrderedHash[arr.group_by { |a| a[:day] }.sort_by(&:first)]

Update: In fact in Ruby 1.9 hash is ordered, so using ActiveSupport extension is not required:

Hash[arr.group_by { |a| a[:day] }.sort_by(&:first)]

Upvotes: 0

Dave Newton
Dave Newton

Reputation: 160321

Personally, I wouldn't bother "sorting" the keys (which amounts to ordering-by-entry-time in Ruby 1.9) until I actually needed to. Then you can use group_by:

arr = [{:day=>4,:name=>'Jay'}, {:day=>1,:name=>'Ben'}, {:day=>4,:name=>'Jill'}]
arr.group_by { |a| a[:day] }
=> {4=>[{:day=>4, :name=>"Jay"}, {:day=>4, :name=>"Jill"}],
    1=>[{:day=>1, :name=>"Ben"}]}

Instead, sort the keys when you actually need them.

Upvotes: 4

knut
knut

Reputation: 27885

Simple answer:

data = [
    {:day=>4,:name=>'Jay'},
    {:day=>1,:name=>'Ben'},
    {:day=>4,:name=>'Jill'}
]

#expected solution
sol = {
    1=>[{:day=>1,:name=>'Ben'}],
    4=>[{:day=>4,:name=>'Jay'},{:day=>4,:name=>'Jill'}]
}

res = {}
data.each{|h|
  res[h[:day]] ||=  [] 
  res[h[:day]] << h
}

p res
p res == sol    #check value
p res.keys == sol.keys  #check order

Problem with this solution: The hash is not sorted as requested. (Same problem has Anurags solution).

So you must modify the answer a bit:

res = {}
data.sort_by{|h| h[:day]}.each{|h|
  res[h[:day]] ||=  [] 
  res[h[:day]] << h
}

p res
p res == sol    #check value
p res.keys == sol.keys  #check order

Upvotes: 0

Related Questions