Sorting an array of hashes

Hi,

I’ve an array of hashes I want to sort, first by key1 and then subsort by
key2. I’ve some code that works but was wondering if there is a more
idiomatic method of doing this in Ruby:

create an array of Hashes. We want to sort by firstKey, then secondKey.

unsorted = []
100.times do |t|
unsorted.push({ “firstKey” => rand(10), “secondKey” => t})
end

get an array of unique firstKeys

firstKeys = []
unsorted.each { |hash| firstKeys.push(hash[“firstKey”]) }
firstKeys.uniq!.sort!

finalArray = []
firstKeys.each do |aKey|

find all the items of aKey

firstKeyMatches = unsorted.find_all { |hash| hash[“firstKey”] == aKey}

sort them and add to the finalArray

finalArray += firstKeyMatches.sort {|x,y| x[“secondKey”] <=>
y[“secondKey”] }
end

the result

finalArray.each { |hash| puts “firstKey [#{hash[“firstKey”].to_s.rjust(4)}]
secondKey [#{hash[“secondKey”].to_s.rjust(4)}]” }

···

===================

regards,

Martin

Martin Stannard martin@massive.com.au writes:

Hi,

I’ve an array of hashes I want to sort, first by key1 and then subsort by
key2. I’ve some code that works but was wondering if there is a more
idiomatic method of doing this in Ruby:

create an array of Hashes. We want to sort by firstKey, then secondKey.

unsorted =
100.times do |t|
unsorted.push({ “firstKey” => rand(10), “secondKey” => t})
end

a shorter one (not tested)

#create a hash of firstKey containing array of k2
top={}
unsorted.each{|k1, k2|
if top[k1]
top[k1] << k2
else
top[k1] = [k2]
end
}
sorted =
top.sort.each{|k1, ak2|
ak2.sort.each {|k2|
sorted << [k1, k2]
}
}
p sorted

YS.

Hi –

Hi,

I’ve an array of hashes I want to sort, first by key1 and then subsort by
key2. I’ve some code that works but was wondering if there is a more
idiomatic method of doing this in Ruby:

One way would be:

unsorted = (0…100).map {|t| { :k1 => rand(10), :k2 => t} }
sorted = unsorted.sort do |a,b|
if (n = a[:k1] <=> b[:k1]).zero?
a[:k2] <=> b[:k2]
else
n
end
end

or, if you don’t have my probably ridiculous aversion to the ternary
operator:

unsorted = (0…100).map {|t| { :k1 => rand(10), :k2 => t} }
sorted = unsorted.sort do |a,b|
(n = a[:k1] <=> b[:k1]).zero? ? a[:k2] <=> b[:k2] : n
end

David

···

On Fri, 11 Oct 2002, Martin Stannard wrote:


David Alan Black | Register for RubyConf 2002!
home: dblack@candle.superlink.net | November 1-3
work: blackdav@shu.edu | Seattle, WA, USA
Web: http://pirate.shu.edu/~blackdav | http://www.rubyconf.com

[…]

create an array of Hashes. We want to sort by firstKey, then secondKey.

unsorted =
100.times do |t|
unsorted.push({ “firstKey” => rand(10), “secondKey” => t})
end

Since all you seem to need is a lexicographic ordering, how about:

unsorted.map{|h|[h[“firstKey”],h[“secondKey”],h.id,h]}.sort.map{|,,_,h|h}

or, in 1.7:

unsorted.sort_by{|h|[h[“firstKey”],h[“secondKey”]]}

The h.id in the first case is necessary to prevent the individual
hashes from being compared, which doesn’t work. In either case, we punt
by relying on the fact that <=> on arrays implements lexicographic
ordering.

[…]

		Reimer Behrends
···

Martin Stannard (martin@massive.com.au) wrote:

Hi,

I’ve an array of hashes I want to sort, first by key1 and then subsort by
key2. I’ve some code that works but was wondering if there is a more
idiomatic method of doing this in Ruby:

create an array of Hashes. We want to sort by firstKey, then secondKey.

unsorted =
100.times do |t|
unsorted.push({ “firstKey” => rand(10), “secondKey” => t})
end

in Ruby 1.7.x

finalArray = unsorted.sort_by {|e| [e[‘firstKey’], e[‘secondKey’]]}

the result

finalArray.each { |hash| puts “firstKey [#{hash[“firstKey”].to_s.rjust(4)}]
secondKey [#{hash[“secondKey”].to_s.rjust(4)}]” }

Hope this helps,

Mike

···

In article C1126400B227D411AC1200A00CC456B5015510E0@emailserver.massive.com.au, Martin Stannard wrote:


mike@stok.co.uk | The “`Stok’ disclaimers” apply.
http://www.stok.co.uk/~mike/ | GPG PGP Key 1024D/059913DA
mike@exegenix.com | Fingerprint 0570 71CD 6790 7C28 3D60
http://www.exegenix.com/ | 75D2 9EC4 C1C0 0599 13DA

I’d like to remind you of the nonzero? method:

unsorted = (0…100).map {|t| { :k1 => rand(10), :k2 => t} }
sorted = unsorted.sort do |a,b|
(a[:k1] <=> b[:k1]).nonzero? || a[:k2] <=> b[:k2]
end

This works in 1.6 too. But if you have Ruby 1.7 and don’t need to
be compatible to 1.6 I’d use Enumerable#sort_by, as others have
shown. Maybe the 1.6 code is a bit more efficient, but I haven’t
tested this.

Regards,
Pit

···

On 11 Oct 2002, at 10:04, dblack@candle.superlink.net wrote:

(first version snipped)

or, if you don’t have my probably ridiculous aversion to the ternary
operator:

unsorted = (0…100).map {|t| { :k1 => rand(10), :k2 => t} }
sorted = unsorted.sort do |a,b|
(n = a[:k1] <=> b[:k1]).zero? ? a[:k2] <=> b[:k2] : n
end