0

I have the following hashes:

WorkersPay:

{"Aaron"=>[300], "Alex"=>[684], ... , "Wendy"=>[792] , "Zach"=>[281]}

and WorkersHrs:

{"Aaron"=>[36], "Alex"=>[34], ... , "Wendy"=>[39], "Zach"=>[42]}

Where the ellipses (...) indicate a countably infinite number of names between, "Alex," and, "Wendy."

I want to accomplish two things:

  1. Pseudo code: puts "#{worker} worked #{hours} and earned $#{dollars}.00" for every worker in both WorkersPay and WorkersHrs
  2. Sort that puts not alphabetically, but by the number of hours worked (if the worker worked zero hours, to list them last and just pseudo code: puts "#{worker} worked #{hours} this week")

I have searched through SO for days and this is my best code so far:

WorkerHrs.each do |key, array|
    WorkerHrs.sort_by { |a, b| a[0] <=> b[0] }
    WorkerPay.each do |key2, array2|
        if key == key2
            if array.inject{|a,i| a*10 + i} == 0
                puts "#{key} worked #{array.inject{|a,i| a*10 + i}} hours this week"
            else
               puts "#{key} worked #{array.inject{|a,i| a*10 + i}} hours and earned $#{array2.inject{|a,i| a*10 + i}}.00"
            end
        end
    end
end

Hoping to achieve this:

John worked 80 hours and earned $36.00
                 .
                 .
                 .
Sam worked 0 hours this week

Where the vertical ellipses indicate the exact same thing as the regular ellipses.

I have never worked with hashes that contain key, array pairs before and would appreciate any and all help getting my horrendous code to work!

MDB
  • 27
  • 4
  • 1
    Are the values in your hashes really arrays of one element? That seems a bit clunky. Why aren't they just values? Like: `{"Aaron"=>300, "Alex"=>684, ... , "Wendy"=>792 , "Zach"=>281}` – lurker Jan 18 '16 at 16:58
  • I didn't include all of my code: those arrays had multiple elements before I totaled them. Would it benefit me to change the way I'm storing the information once the sorting is done? – MDB Jan 18 '16 at 17:02
  • 1
    The requirements just need to be made clear. For the sake of this question, are the hash values then really just one number, not an array of 2 or more values? And also note: a Hash is *not* an ordered collection. There's no such thing as a sorted Hash. In Ruby, if you sort a Hash, it becomes an Array, which is ordered. – lurker Jan 18 '16 at 17:07
  • That explains why I haven't been able to sort the output. So for the sake of this question yes: both hashes contain key, array pairs with only one element. If I need to turn both hashes into multidimensional arrays to sort them, what would be the best way to do so given what I need to `puts`? – MDB Jan 18 '16 at 17:15
  • 1
    While I'm still trying to figure out what *inifinitely finite* means, here's just another comment: you should be using `==` not `===`. `===` is the [*case subsumption operator*](http://stackoverflow.com/questions/4467538/what-does-the-operator-do-in-ruby). The `===` might happen to work here, but it's not the right use of the operator. – lurker Jan 18 '16 at 17:20
  • Since a Hash has unique keys, why do you need `array.inject` to total up hours? – lurker Jan 18 '16 at 17:21
  • I edited my question to make both corrections that you pointed out, and I found the `array.inject` part of my code from another question here. When I `puts "#{array}"` I didn't want to see the brackets around the numbers. is there a cleaner way of doing this? – MDB Jan 18 '16 at 17:30
  • `puts #{array[0]}` or `puts #{array.first}` will print the first element of an array. – lurker Jan 18 '16 at 17:35

1 Answers1

1

Note that in Ruby, if you sort a Hash, then Ruby will first convert it to an array of arrays where the "inner" arrays are of 2 elements each: the key and the value.

To sort the WorkHrs, then, you only need this:

SortHrs = WorkHrs.sort_by { |wh| wh[1] }.reverse  # reverse order of hours

This says you want to sort your array of arrays by the second element of each internal array. This is short-hand for, WorkHrs.sort_by { |a,b| a[1] <=> b[1] } since a <=> b is the default comparison method.

Implicitely, Ruby will make WorkHrs look like this before sorting:

[["Aaron", [36]], ["Alex", [34]], ... , ["Wendy", [39]], ["Zach", [42]]]

wh[1] is the value of the key-value pair of the array element (wh[0] would be the key).

Then you process it:

SortHrs.each do |wh|
  if wh[1] == 0     # Hours worked is 0?
    puts "#{wh[0]} worked 0 hours this week"
  else
    puts "#{wh[0]} worked #{wh[1]} hours and earned $#{WorkersPay[wh[0]].first}"
  end
end

Here, WorkersPay[wh[0]] uses wh[0] (the person's name) as a key to access the hash for pay. The .first gets the first (and, in this case, the only) element. You could also use, WorkersPay[wh[0]][0].

That's about all there is to it. I will leave the formatting of the pay amount as an exercise. Look up Ruby string formatting for information.

lurker
  • 56,987
  • 9
  • 69
  • 103
  • I attempted your exact solution, but ended up receiving this error: `undefined method 'sort_by!' for {"Aaron"=>[300], "Alex"=>[684],` ... `"Wendy"=>[792], "Zach"=>[281]}:Hash` and on that line I got an `initialize` error. After searching around a bit more, a more clumsy solution worked: `WorkerHrs = (WorkerHrs.sort_by { |wh| wh[1] }).reverse`. I don't know why your solution didn't work and maybe I am using an outdated IDE, but thank you for all the time and effort you put into helping me today. – MDB Jan 18 '16 at 22:32
  • @MDB yes, sorry, in this case since it's a Hash, you can't use the exclamation point version which changes the item. You have to make a new copy. See my updated answer. Your approach of reusing `WorkerHrs` will work as well. – lurker Jan 18 '16 at 22:40