0

I am new to Ruby, and working on code written by someone else. We are having to work with deprecated versions: Ruby 1.8.7, and Rails 2.3.5 - because this is a charity in the developing world and there is no capacity to upgrade.

I have a report which produces a table (a drug inventory), and need to sort it by the drug's name.

The relevant controller code looks like this:

all_drugs = Drug.find(:all,
                      :order => "name ASC")
@stock = {}
all_drugs.each{ |drug|
  drug_id = drug.drug_id
  first_date = Pharmacy.active.find(:first,
                                    :conditions =>["drug_id =?",drug_id],
                                    :order => "encounter_date").encounter_date.to_date rescue nil
  next if first_date.blank?
  next if first_date > @end_date

  start_date = @start_date
  end_date = @end_date

  stock_at = Pharmacy.stock_at(drug_id,end_date)
  if stock_at > 0
    last_activity = "In stock"
    else last_activity = Pharmacy.last_activity(drug_id,end_date)
  end

  drug = Drug.find(drug_id)
  drug_name = drug.name
  @stock[drug_name] = {"dispensed" => 0,"stock_at" => 0,"removed" => 0, "receipts" => 0,"prescribed" => 0,"last_activity" => 0}
  @stock[drug_name]["dispensed"] = Pharmacy.dispensed_drugs_since(drug.id,start_date,end_date)
  @stock[drug_name]["stock_at"] = stock_at
  @stock[drug_name]["removed"] = Pharmacy.total_removed(drug.id,start_date,end_date)
  @stock[drug_name]["receipts"] = Pharmacy.total_delivered(drug.id,start_date,end_date)
  @stock[drug_name]["prescribed"] = Pharmacy.prescribed_drugs_since(drug.id,start_date,end_date)
  @stock[drug_name]["last_activity"] = last_activity
}

The relevant view code looks like this:

<%count = 1
@stock.each{|name,values|
  prescribed = values["prescribed"]
  receipts = values["receipts"]
  dispensed = values["dispensed"]
  removed = values["removed"]
  stock_at = values["stock_at"]
  last_activity = values["last_activity"]
%>

  <tr>
    <td class="color_<%=color%>"><%=name%></td>
    <td class="color_<%=color%> caldata" id="stock_at_<%=count%>"><%=stock_at%></td>
    <td class="color_<%=color%> caldata" id="removed_<%=count%>"><%=removed%></td>
    <td class="color_<%=color%> caldata" id="prescribed_<%=count%>"><%=prescribed%></td>
    <td class="color_<%=color%> caldata" id="dispensed_<%=count%>"><%=dispensed%></td>
    <td class="color_<%=color%> caldata" id="last_activity_<%=count%>"><%=last_activity%></td>
    <td class="color_<%=color%> caldata" id="receipts_<%=count%>"><%=receipts%></td>
  </tr>
  <% count+=1
}%>

This happily produces the correct table but in a random order (re-starting the server and reloading the page will give the table in a new order).

I have tried a variety of options found online. I tried @stock.all.order (doesn't work in Rails 2.x), @stock.find (doesn't accept 2 arguments, :all and :order, and using just :order doesn't help), @stock.sort (no effect), and @stock.sort_by (no effect). All of those placed either at the end of the controller code or just before the @stock.each in the view code.

I have to say I am particularly baffled that the order is random - I would have presumed that even if I was failing to order it by name, the order would still be consistent...

Any help would be very much appreciated!

4
  • Did you try .sort ? Commented Jun 1, 2016 at 9:12
  • Yep, thank you, I tried both .sort and .sort_by on @stock - I'll update the question to clarify that. I tried a simple @stock.sort, as well as some variations such as @stock.sort_by { |a| a[0] }, and @stock.sort{|a,b| b[:name] <=> a[:name]}, based on other answers I'd found on this site. None worked, but of course I may have got the syntax wrong. In particular I am not confident that I fully understand the structure and indexing of the @stock array. Commented Jun 1, 2016 at 9:22
  • @stock is not an array. It is an instance variable, that was declared as a Hash: @stock = {}. You probably want to @stock.sort_by { |k,v| v }, or just @stock.keys.sort.each { |k| } Commented Jun 1, 2016 at 9:57
  • Thank you for clarifying that for me, I had missed that distinction! Unfortunately neither of these solutions worked. Commented Jun 1, 2016 at 13:33

2 Answers 2

1

Hashes are unordered by definition in ruby 1.8.7(please check Ruby 1.8: Hash#sort not return hash but array (better way to do this?)) hence we have to use the order as array. There can be no such thing as a sorted Hash. in your view change the line

@stock.each{|name,values|

to

@stock.sort_by {|s, v| s.downcase}.each{|name,values|
Sign up to request clarification or add additional context in comments.

14 Comments

Thank you, but this made no difference - the list is still unsorted.
can you dump the "@stock" variable and send me the first 3 rows, 'puts @stock'
Start of the dump looks like this (dummy data of course): {"Paracetamol (250mg)"=>{"receipts"=>10000.0, "prescribed"=>0, "dispensed"=>0, "last_activity"=>"In stock", "removed"=>0, "stock_at"=>10000.0}, "Paracetamol (1g)"=>{"receipts"=>10000.0, "prescribed"=>0, "dispensed"=>0, "last_activity"=>"In stock", "removed"=>0, "stock_at"=>10000.0}, "EFV (Efavirenz 600mg tablet)"=> N.B. I reloaded the server a couple of times and each time the order of the drugs varies.
Please try Hash[@stock.sort_by {|s, v| s.downcase}].each{|name,values| -- Don't forget to add the v in |s, v|
I suspect that as another hash is being created the issue with unordered hash in ruby 1.8.7 is still generating a 'random' order of drugs in the view.
|
1

Hash in Ruby 1.8.7 was not ordered, so you can't rely on the order of the keys in @stock.

As a workaround, first I'd query Drug selecting only id and name fields

all_drugs = Drug.find(:all, :select => "id, name")

as you are querying for single drug attributes later in the code.

Then I'd create an empty @stock_keys array to collect drug names, just after the @stock hash definition

@stock = {}
@stock_keys = []

and push drug names in it, right after your query for drug_id

drug = Drug.find(drug_id)
drug_name = drug.name
@stock_keys << drug_name 
@stock[drug_name] = ...

At the end of the iteration on all_drugs, in your controller, I'd sort the @stock_keys array

@stock_keys.sort!

Then, in the view, I'd iterate over the @stock_keys array, retrieving at each step the drug's values from the @stock hash

 @stock_keys.each do |name|
    values = @stock[name] 
    prescribed = values["prescribed"]
    ...
 end

I hope this can help.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.