Search code examples
arrayssortinghashnestedputs

Ruby: Sorting and, "puts," by Looping Through Multiple Hashes Containing Arrays


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!


Solution

  • 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.