[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Sort by object attribute

Posted on 2008-11-06
18
Medium Priority
?
430 Views
Last Modified: 2010-04-21
Let me start by saying that my ultimate goal is to have a listing of items grouped by release date (using distance_of_time_in_words) and sorted in chronological order.

This is what I've done so far and it's not really working:
1) I have a model that has a release_date attribute
2) In the controller, converted the release_date value to distance_of_time_in_words
3) In the controller grouped all the objects with the same distance_of_time_in_words values together using group_by
4) In the view used model_name.keys.sort but what I get is an alphabetical sorting of the distance_of_time_in_words values (i.e. "2 Months", "5 Days", "6 Days", "about a month"). They do not sort chronologically.

So now my question is, how do I do a sort by an attribute of a value in a hash? What I want to do is sort by the model_name.release_date value. How do I structure the sort call to do that?
0
Comment
Question by:victornegri
  • 10
  • 5
  • 3
18 Comments
 
LVL 10

Expert Comment

by:Andrew Doades
ID: 22899148
"So now my question is, how do I do a sort by an attribute of a value in a hash?"

The attached code might not be 100% what you want, but I think it should give a clear idea.
hash = Hash.new
hash['al'] = 'alexander'
hash['barney'] = 'rubble'
hash['fred'] = 'flinstone'
hash['john'] = 'doe'
 
# sort the hash by value, and print the results in the sorted order.
hash.sort{|a,b| a[1]<=>b[1]}.each { |elem|
  puts "#{elem[1]}, #{elem[0]}"
}
 
#this would be the result
alexander, al
doe, john
flinstone, fred
rubble, barney

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22899614
But how about if the value is an object and I want to sort by one of that object's attributes?

myObject1 = myClass.new
myObject1.someAttribute = "Test Value"
myObject1.save
 
myObject2 = myClass.new
myObject2.someAttribute = "Test Value 2"
myObject2.save
 
myHash = Hash.new
hash['key1'] = myObject1
hash['key2'] = myObject2

Open in new window

0
 
LVL 10

Expert Comment

by:Andrew Doades
ID: 22899658
would it not be like ?
myHash = Hash.new
hash['key1'] = myObject1.someAttribute
hash['key2'] = myObject2.someAttribute

Open in new window

0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 10

Author Comment

by:victornegri
ID: 22899707
No. My hash has the object as the value. That's what group_by does.
0
 
LVL 10

Expert Comment

by:Andrew Doades
ID: 22899791
Would the below not work strait off?

I am looking at this page here

http://www.crummy.com/writing/RubyCookbook/test_results/11049.html
myObject1 = myClass.new
myObject1.someAttribute = "Test Value"
myObject1.save
 
myObject2 = myClass.new
myObject2.someAttribute = "Test Value 2"
myObject2.save
 
myHash = Hash.new
hash['key1'] = myObject1
hash['key2'] = myObject2

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22899857
My question is about sorting a hash using the object attribute, not about creating a hash (unless I'm misunderstanding your comment).
0
 
LVL 10

Expert Comment

by:Andrew Doades
ID: 22899940
I think that we are both getting a little confused, no offence!!

Hopefully the below code should create the objects, then hash the entered value and sore the hashed values.

I personally can't see why it would not work, unless I am missing something?!
myObject1 = myClass.new
myObject1.someAttribute = "Test Value"
myObject1.save
 
myObject2 = myClass.new
myObject2.someAttribute = "Test Value 2"
myObject2.save
 
myHash = Hash.new
hash['key1'] = myObject1.someAttribute
hash['key2'] = myObject2.someAttribute
 
myHash.sort{|a,b| a[1]<=>b[1]}.each { |elem|
  puts "#{elem[1]}, #{elem[0]}"
}

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22899982
FYI, the code I typed above is not the real code. It was just something I typed to illustrate my point. I tried what you suggested earlier and it would not work. I don't know how to specify to sort on the attribute and not on the primary key or anything like that. I tried this too but got an error:

myHash.sort{|a,b| a[1].myAttribute<=>b[1].myAttribute}.each { |elem|
  puts "#{elem[1]}, #{elem[0]}"
}


Here's some real code (I'm aware that I'm sorting by keys right now. I want to switch it to value but I keep getting an error):

In the controller:
 
@items = Category.all_category_items.group_by {|i| distance_of_time_in_words(Time.now, i.release_date)}
 
In the view:
 
<% @items.keys.sort.each do |release_date| %>
<h2><%= release_date.titleize %></h2>
<ul>
	
	<% for item in @items[release_date] %>
		<li><%= Category.find(item.category_id).name + ": " + link_to(item.name, item) + ": " +  item.release_date %></li>
	<% end %>
</ul>
<% end %>

Open in new window

0
 
LVL 10

Expert Comment

by:Andrew Doades
ID: 22900146
OK done some reading and I think this is what you want?!

Look at this part

hash.sort{|a,b| a[1]<=>b[1]}.each { |elem|
  puts "#{elem[1]}, #{elem[0]}"
}

then change to this

hash.sort{|a,b| b[1]<=>a[1]}.each { |elem|
  puts "#{elem[1]}, #{elem[0]}"
}

This should reverse the order that the results are ordered, is this what you want?

Of course change this to your code, etc. but keep the a and b bits in the same "layout"
0
 
LVL 10

Author Comment

by:victornegri
ID: 22900270
Nope. Don't want to reverse the order. I want to sort based on an attribute of the object.
0
 
LVL 2

Expert Comment

by:karfi
ID: 22933113
What is the error message you got when you tried this:

myHash.sort{|a,b| a[1].myAttribute<=>b[1].myAttribute}.each { |elem|
  puts "#{elem[1]}, #{elem[0]}"
}

What is the type of the myAttribute member?
Maybe in the class of myAttribute the <=> operator is missing.
class MyAttr
 
  attr_accessor :something
 
  # ....
 
  # maybe you need this?
  def <=>(other)
    return something<=>other.something
  end
end
 
class MyClass
  attr_accessor :myAttribute
 
  # ....
 
  def initialize()
     myAttribute = MyAttr.new()
  end
end

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22933326
I get an "undefined method 'myAttribute' in Array..." error when I try the above statement.

myAttribute is of the type 'date'

How would I add the <=> operator to the myAttribute member?

BTW, myAttribute is part of the example code we were working with. Can you look at the real code I posted above to see what I need to do there? myAttribute is equivalent to release_date in the real code.
0
 
LVL 2

Expert Comment

by:karfi
ID: 22933970
OK, I think I see the problem:

the group_by method returns a Hash object, where the keys are what distance_of_time_in_words calls return, and the values are Arrays which are containing the objects with the same "distance_of_time_in_words" values:

"6 Days" => [ obj9, obj7],
"2 Months" => [ obj1, obj6, obj2, ...],
"5 Days" => [ obj2, obj3]

So you can use the first object of the array for sorting:
model_name.sort { |a, b|
 a[1][0].release_date<=>b[1][0].release_date
}.each { |elem|
 puts "#{elem[0]} => #{elem[1]}"
}

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22934087
So this is what I changed it to and I get this error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each

<% @items.sort{|a,b| a[1][0].release_date<=>b[1][0].release_date}.each { |release_date| %>
<h2><%= release_date%></h2>
<ul>
	
	<% for item in @items[release_date] %>
		<li><%= Category.find(item.category_id).name + ": " + link_to(item.name, item) + ": " + distance_of_time_in_words(Time.now, item.release_date) %></li>
	<% end %>
</ul>
<% } %>

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22934126
Do I have to tell it somehow to sort by the value of the hash and not by the key?
0
 
LVL 2

Accepted Solution

by:
karfi earned 2000 total points
ID: 22934192
when iterating over a Hash (@items here), you get the key, value pair in the each block. (Or an Array with two items (key and value) when using only one placeholder.

use each_key in your code or see the following:
<% @items.sort{|a,b| a[1][0].release_date<=>b[1][0].release_date}.each { |release_date, data_array| %>
<h2><%= release_date%></h2>
<ul>
        
        <% for item in data_array %>
                <li><%= Category.find(item.category_id).name + ": " + link_to(item.name, item) + ": " + distance_of_time_in_words(Time.now, item.release_date) %></li>
        <% end %>
</ul>
<% } %>

Open in new window

0
 
LVL 10

Author Comment

by:victornegri
ID: 22934233
Awesome! Not only did you solve my problem but you helped me understand how sort and group_by work! Thanks!
0
 
LVL 10

Author Closing Comment

by:victornegri
ID: 31514020
Thanks!
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In Ruby, Call or invoke a API DLL library is easily via Win32API class, win32-api gem or other gems. For general DLL API call, there are quite a few references, some good tips list below: http://www.rubytips.org/2008/05/13/accessing-windows-api-fro…
Recently I spent hours debugging an issue in a Rails project where ActiveRecord was causing MySQL errors trying to create a User object of a class at the top level of a Single Table Inheritance model structure.  It turns out `.create` behaves differ…
Screencast - Getting to Know the Pipeline
As many of you are aware about Scanpst.exe utility which is owned by Microsoft itself to repair inaccessible or damaged PST files, but the question is do you really think Scanpst.exe is capable to repair all sorts of PST related corruption issues?
Suggested Courses
Course of the Month18 days, 13 hours left to enroll

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question