Forum: Ruby nuby question: sorting with nils

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
D4b246038154d7cc2363256bd25a4fe0?d=identicon&s=25 larry (Guest)
on 2006-03-09 15:49
(Received via mailing list)
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.
05be5d6610e2c3f1780aa0e39e902e93?d=identicon&s=25 Farrel Lifson (Guest)
on 2006-03-09 16:01
(Received via mailing list)
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
05be5d6610e2c3f1780aa0e39e902e93?d=identicon&s=25 Farrel Lifson (Guest)
on 2006-03-09 16:11
(Received via mailing list)
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
159cd33667611645164e8623de42f67e?d=identicon&s=25 Toby DiPasquale (Guest)
on 2006-03-09 16:39
Farrel Lifson 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).

<snip>
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: {#<struct X a=1, b=2>, #<struct X a=2, b=1>, #<struct X
a=3, b=4>, #<struct X a=3, b=3>}>
1
1
1
puma:~>
</snip>

--
Toby DiPasquale
D4b246038154d7cc2363256bd25a4fe0?d=identicon&s=25 larry (Guest)
on 2006-03-09 17:06
(Received via mailing list)
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.
A4a4095ff08bd0fced3c3fddbeac743a?d=identicon&s=25 Cameron McBride (Guest)
on 2006-03-09 17:18
(Received via mailing list)
On 3/9/06, Toby DiPasquale <toby@cbcg.net> 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 }
  #<struct X a=1, b=2>
  #<struct X a=3, b=3>
  #<struct X a=3, b=4>
  #<struct X a=4, b=2>
  #<struct X a=4, b=8>

Cameron
05be5d6610e2c3f1780aa0e39e902e93?d=identicon&s=25 Farrel Lifson (Guest)
on 2006-03-09 17:21
(Received via mailing list)
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: {#<struct X a=1, b=2>, #<struct X a=2, b=1>, #<struct X
a=3, b=3>,
#<struct X a=3, b=4>}>
-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
This topic is locked and can not be replied to.