Nuby question: sorting with nils


#1

if i make an object sortable using <=> as so:

def <=> anItem
@date <=> anItem.date
end

and date is nil, it fails. What’s the right way to handle nils in this
scenario?

thanks.


#2

def <=> anItem
@date.nil? ? -1 : @date <=> anItem.date
end

You can set the -1, to 0, or 1 depdending where you want the objects
with nil @dates placed.

Farrel


#3

Actually you can shorten it a bit further if you use

@date ? @date <=> anItem.date : -1

That’s due to the fact that in Ruby nil is equivalent to false in
conditional expression.

Farrel


#4

Farrel L. wrote:

Actually you can shorten it a bit further if you use

@date ? @date <=> anItem.date : -1

That’s due to the fact that in Ruby nil is equivalent to false in
conditional expression.

Speaking of this very topic, I am having an issue where I want to sort
objects by two of its attributes in a SortedSet, but its not working
out:

I’d like it to be sorted first by its :a attribute, but if they are
equal, by whatever sorting on the :b attributes comes up with. Anybody
know what I’m doing wrong in this test code? The third and fourth
elements are not in the proper order (excuse the overly verbose <=>
method… I was trying to get it working).

puma:~> cat settest #!/usr/bin/env ruby X = Struct.new :a, :b class X def <=> other if not @a 1 elsif not @b -1 else if @a == other.a @b <=> other.b else @a <=> other.a end end end end

require ‘set’
s = SortedSet.new
s << X.new( 1, 2)
s << X.new( 2, 1)
s << X.new( 3, 3)
s << X.new( 3, 4)
s << X.new( 1, 2)
p s

x = X.new 3, 3
y = X.new 3, 4
p x <=> y
p y <=> x
p x <=> x

puma:~> ./settest
#<SortedSet: {#, #, #, #}>
1
1
1
puma:~>


Toby DiPasquale


#5

On 3/9/06, Toby DiPasquale removed_email_address@domain.invalid wrote:

Speaking of this very topic, I am having an issue where I want to sort
objects by two of its attributes in a SortedSet, but its not working
out:

I’d like it to be sorted first by its :a attribute, but if they are
equal, by whatever sorting on the :b attributes comes up with.

as a quick alternative, sorting by several attributes is pretty easy
using #sort_by

X = Struct.new :a, :b
s = []
s << X.new(3, 3)
s << X.new(3, 4)
s << X.new(1, 2)
s << X.new(4, 8)
s << X.new(4, 2)
sorted = s.sort_by { |a| [a.a,a.b] }
sorted.each { |v| p v }
#
#
#
#
#

Cameron


#6

Actually, the weird thing is that the dates were never nil, but the
sort thinks they are. Adding the required nil checking code suppressed
the exception but the sort is returning things in random order because
it sees each date as nil.

I posted a followup on the rails list because I thought it might be a
rails issue. They suggested I use an explicit sort block instead:

@assignments= @assignments.sort {|a,b| a.date <=> b.date}

and it works. Not sure why.


#7

I don’t think you can access the attributes of a Struct from inside it
using object variables (like @a).

C:\Documents and Settings\flifson\Desktop>irb
irb(main):001:0> X = Struct.new :a,:b
=> X
irb(main):002:0> class X
irb(main):003:1> def print_attributes
irb(main):004:2> puts “#{@a}:#{b}”
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> s = X.new ‘hello’,‘world’
=> #<struct X a=“hello”, b=“world”>
irb(main):008:0> s.print_attributes
:world

Notice only the value of the b attribute was printed (‘world’).
That’s because self. was implicitly added to the front of it, so it
became self.b which will return the correct value. Changing it to:

def <=> other
if not a
1
elsif not b
-1
else
if a == other.a
b <=> other.b
else
a <=> other.a
end
end
end

produces:

C:\Documents and Settings\flifson>ruby settest.rb
#<SortedSet: {#, #, #,
#}>
-1
1
0

My guess is that the Struct is really a type of Hash and that it uses
the method_missing system hook to actually retrieve the data from
whatever it uses to store the data. It has a lot of Hash like methods
such as values, each_pair etc etc.

Farrel