How to sort this hash?


#1

Is it possible to sort the jobs hash below based on values such as
command, start_time and stop_time?

Ideally, sort_by would be dynamically provided and I would output the
jobs entries based on the value defined for sort_by.

--------------------------(basic_script.rb)------------------------
sort_by = “start_time” #<---- I’d like to use something like this to
define which job is listed first

jobs = {
“2231” => {“command” => “test_a.bash”,
“start_time” => “20051211”,
“stop_time” => “20051222”},
“1131” => {“command” => “test_b.bash”,
“start_time” => “20051011”,
“stop_time” => “20051122”},
“231” => {“command” => “test_c.bash”,
“start_time” => “20051215”,
“stop_time” => “20051227”}
}

jobs.each do |job, attributes|
puts “------------------”
puts attributes[“command”]
puts attributes[“start_time”]
puts attributes[“stop_time”]
end
--------------------------(/basic_script.rb)------------------------

This is the default output:
./basic_script.rb

test_c.bash
20051215
20051227

test_b.bash
20051011
20051122

test_a.bash
20051211
20051222

Id like to have the dynamics of say, ordering these by attribute such
as start_time:

(I wish I could get this)
test_b.bash
20051011
20051122

test_a.bash
20051211
20051222

test_c.bash
20051215
20051227

Any suggestions?

thanks in advance.


#2

x1 wrote:

  "2231" => {"command" => "test_a.bash",
  		"start_time" => "20051211",
  		"stop_time" => "20051222"},
  "1131" => {"command" => "test_b.bash",
  		"start_time" => "20051011",
  		"stop_time" => "20051122"},
  "231" => {"command" => "test_c.bash",
  		"start_time" => "20051215",
  		"stop_time" => "20051227"}

}

sort_keys = [“start_time”, “stop_time”]
jobs.sort_by do |job|
sort_keys.map do |key|
job[key]
end
end

Note that this will only work as long as all sort_keys are present in
every job.

You might want to check the documentation for Enumerable#sort_by, but
let me document the special thing in this case as well.

sort_by’s block can return multiple values as an Array. In that case it
will first sort by the first value then by the second one and so on.
This is used in the above sample where every job is sorted by the fields
representing the sort_keys on that specific job.


#3

x1 wrote:

Is it possible to sort the jobs hash below based on values such as
command, start_time and stop_time?

Ideally, sort_by would be dynamically provided and I would output the
jobs entries based on the value defined for sort_by.

This should do:

jobs.sort{|a,b|a[1][sort_by] <=> b[1][sort_by]}.each do |k, attributes|
puts “------------------”
puts attributes[“command”]
puts attributes[“start_time”]
puts attributes[“stop_time”]
end

cheers

Simon


#4

Simon!! That worked :smiley: huge grin

If you don’t mind me asking, have you had to use this before? I
wouldnt consider myself a top notch developer but between the ruby
docs and google searches, I would have never figured this out…

Any tips out how you did? Just… development experience? Thanks again!!


#5

Simon, would you be willing to explain the syntax you used here:

jobs.sort{|a,b|a[1][sort_by] <=> b[1][sort_by]}.each do |k, attributes|

I just can’t quite understand the [sort_by] usage here…?

Thanks (and apologies for the newbieness :slight_smile:

Jeff


#6

x1 wrote:

I tried adding your suggestions but ended up with an error:

Ah, I was assuming jobs to be an Array in error.

Please try this instead:

sort_keys = [“start_time”, “stop_time”]
jobs.sort_by do |key, job|
sort_keys.map do |key|
job[key]
end
end


#7

Jeff C. wrote:

jobs.sort{|a,b|a[1][sort_by] <=> b[1][sort_by]}.each do |k, attributes|
I just can’t quite understand the [sort_by] usage here…?

It looks up the value in the hash from a[1] (a[0] is “2231” / “1131” /
“231” for the sample data) that represents to the key identified by
sort_by.

So if sort_by is “command” it will sort the jobs by the value
corresponding to their “command” key. Which is “test_a.bash” for job
“2231”, “test_b.bash” for job “1131”, “test_c.bash” for job “231” and so
on…


#8

Thank you for the reply.

I tried adding your suggestions but ended up with an error:

ruby array_test.rb
array_test.rb:19:in []': cannot convert String into Integer (TypeError) from array_test.rb:19 from array_test.rb:18:inmap’
from array_test.rb:18
from array_test.rb:17:in sort_by' from array_test.rb:17:ineach’
from array_test.rb:17:in `sort_by’
from array_test.rb:17
Exit code: 1

The script is:


jobs = {
“2231” => {“command” => “test_a.bash”,
“start_time” => “20051211”,
“stop_time” => “20051222”},
“1131” => {“command” => “test_b.bash”,
“start_time” => “20051011”,
“stop_time” => “20051122”},
“231” => {“command” => “test_c.bash”,
“start_time” => “20051215”,
“stop_time” => “20051227”}
}

sort_keys = [“start_time”, “stop_time”]
jobs.sort_by do |job|
sort_keys.map do |key|
job[key]
end
end


#9

So if sort_by is “command” it will sort the jobs by the value

Ah! I thought it was referring to something related to
Enumerable.sort_by.

Got it. Thanks.


#10

jobs.sort{|a,b|a[1][sort_by] <=> b[1][sort_by]}.each do |k, attributes|
puts “------------------”
puts attributes[“command”]
puts attributes[“start_time”]
puts attributes[“stop_time”]
end

Im a newb but i wouldnt have thought a[1][sort_by] would have worked. I
tried accessing this hash in irb in the same way

a = {“job” = > {“wage” => 30, “time” => 60}}

a[1][“wage”] and got an error
NoMethodError: undefined method `[]’ for nil:NilClass

why does it work??? Can you only access a hash like that within a sort
block?


#11

just found the answer to my question here

http://www.ruby-forum.com/topic/163431#718012

for sort, ruby converts a hash to an array as hashes are not orderable
(is that a word??). Therefore accessing what seems to be a hash like an
array is in fact ok as it really is an array.