Array.sort when it's items are String inheritors with redefined <=> works like if not redefined

Discussion in 'Ruby' started by MiG, Oct 19, 2005.

  1. MiG

    MiG Guest

    Hello,
    I want to have a string which, if in array, will be sorted like numbers=
    =20
    I wrote this:

    -------------------------------------------------------------------------=
    ----------------

    class String2 < String

    def <=3D> str2
    self.to_i <=3D> str2.to_i
    end

    end

    a =3D [ String2.new('1'), String2.new('10'), String2.new('5') ]

    puts a.sort.join(',')

    -------------------------------------------------------------------------=
    ----------------

    It produces "1,10,5", but I expected "1,5,10"

    Then I wrote a class without String inheritance and it works.
    BUT: another strange thing happened:

    in `<=3D>': undefined method `to_i' for #<S:0x40020930 @a=3D"10"> =20
    (NoMethodError)

    to_i method, even if @a is a String, must be explicitely defined.
    Moreover "defined". What do you think about it?

    -------------------------------------------------------------------------=
    ----------------

    class String2

    def initialize a
    @a =3D a
    end

    def <=3D> b
    @a.is_a? String # >> true but..
    @a.to_i <=3D> b.to_i # .. @a.to_i doesn't work if I don't defin=
    e =20
    to_i method below
    end

    def to_i
    @a.to_i
    end

    def to_s
    @a
    end

    end

    a =3D [ String2.new('1'), String2.new('10'), String2.new('5') ]

    puts a.sort.join(',')

    -------------------------------------------------------------------------=
    ----------------

    Thank you,
    jan molic
     
    MiG, Oct 19, 2005
    #1
    1. Advertising

  2. MiG wrote:
    > Hello,
    > I want to have a string which, if in array, will be sorted like
    > numbers
    > I wrote this:
    >
    > ------------------------------------------------------------------------

    -----------------
    >
    > class String2 < String
    >
    > def <=> str2
    > self.to_i <=> str2.to_i
    > end
    >
    > end
    >
    > a = [ String2.new('1'), String2.new('10'), String2.new('5') ]
    >
    > puts a.sort.join(',')
    >
    > ------------------------------------------------------------------------

    -----------------
    >
    > It produces "1,10,5", but I expected "1,5,10"


    I think there is an optimization going on that doesn't use <=> for String
    and subclasses. This is one of the reasons why it's subclassing of core
    classes like String, Array etc. should be done rarely and with care.

    In your case you better use sort_by:

    >> a=["1","10","5"]

    => ["1", "10", "5"]
    >> a.sort_by {|x| x.to_i}

    => ["1", "5", "10"]

    This works also if the array contains instances of your subclass. It
    might also be more efficient as #to_i is only invoked once per instance
    and not once per comparison per compared object.

    > Then I wrote a class without String inheritance and it works.
    > BUT: another strange thing happened:
    >
    > in `<=>': undefined method `to_i' for #<S:0x40020930 @a="10">
    > (NoMethodError)
    >
    > to_i method, even if @a is a String, must be explicitely defined.
    > Moreover "defined". What do you think about it?


    That's not strange. That's perfectly normal. Because there is no default
    #to_i method:

    >> Object.new.to_i

    NoMethodError: undefined method `to_i' for #<Object:0x101d0b28>
    from (irb):3
    from :0

    Kind regards

    robert
     
    Robert Klemme, Oct 19, 2005
    #2
    1. Advertising

  3. Hi,

    In message "Re: Array.sort when it's items are String inheritors with redefined <=> works like if not redefined"
    on Wed, 19 Oct 2005 22:28:29 +0900, MiG <> writes:

    |Hello,
    | I want to have a string which, if in array, will be sorted like numbers.
    |I wrote this:
    |
    |-----------------------------------------------------------------------------------------
    |
    |class String2 < String
    |
    | def <=> str2
    | self.to_i <=> str2.to_i
    | end
    |
    |end
    |
    |a = [ String2.new('1'), String2.new('10'), String2.new('5') ]
    |
    |puts a.sort.join(',')

    Why not use sort_by, much simpler solution?

    a = ['1', '10', '5']
    puts a.sort_by{|x|x.to_u}.join(',')

    matz.
     
    Yukihiro Matsumoto, Oct 19, 2005
    #3
  4. On Oct 19, 2005, at 8:59 AM, Yukihiro Matsumoto wrote:

    > Why not use sort_by, much simpler solution?
    >
    > a = ['1', '10', '5']
    > puts a.sort_by{|x|x.to_u}.join(',')


    I believe that second line is supposed to read:

    puts a.sort_by{|x|x.to_i}.join(',')

    James Edward Gray II
     
    James Edward Gray II, Oct 19, 2005
    #4
  5. MiG

    Ara.T.Howard Guest

    Re: Array.sort when it's items are String inheritors with redefined<=> works like if not redefined

    On Wed, 19 Oct 2005, MiG wrote:

    > Hello,
    > I want to have a string which, if in array, will be sorted like numbers. I
    > wrote this:
    >
    > -----------------------------------------------------------------------------------------
    >
    > class String2 < String
    >
    > def <=> str2
    > self.to_i <=> str2.to_i
    > end
    >
    > end
    >
    > a = [ String2.new('1'), String2.new('10'), String2.new('5') ]
    >
    > puts a.sort.join(',')


    put the method in Array:

    harp:~ > cat a.rb
    class Array
    def sort_as!
    map!{|elem| yield elem}
    sort!
    self
    end
    def sort_as
    dup.sort!
    end
    end


    a = %w( 1 10 5 )

    p a.sort_as{|s| Integer s}
    p a.sort_as{|s| Float s}
    p a.sort_as{|s| s.reverse }

    a.sort_as!{|s| Integer s}
    p a


    harp:~ > ruby a.rb
    ["1", "10", "5"]
    ["1", "10", "5"]
    ["1", "10", "5"]
    [1, 5, 10]

    this avoids calling to_i, to_f, or whatever multiple times on the same object,
    which will occur if you use either a spacship (<=>) operator or sort_by
    approach.


    hth.


    -a
    --
    ===============================================================================
    | email :: ara [dot] t [dot] howard [at] noaa [dot] gov
    | phone :: 303.497.6469
    | anything that contradicts experience and logic should be abandoned.
    | -- h.h. the 14th dalai lama
    ===============================================================================
     
    Ara.T.Howard, Oct 19, 2005
    #5
  6. On 10/19/05, Ara.T.Howard <> wrote:
    > this avoids calling to_i, to_f, or whatever multiple times on the same ob=

    ject,
    > which will occur if you use either a spacship (<=3D>) operator or sort_by
    > approach.


    I thought sort_by was a packaged form of the Schwartzian transform, i.e.

    class A
    def to_i
    puts "in A.to_i"
    3
    end
    end

    p [1, 2, 3, 4, 5, A.new].sort_by {|x| x.to_i }
    # is equivalent to
    p [1, 2, 3, 4, 5, A.new].map{|x| [x.to_i, x]}.sort{|y, z| y[0] <=3D>
    z[0]}.map{|x| x[1]}

    __END__
    in A.to_i
    [1, 2, 3, #<A:0x2870f28>, 4, 5]
    in A.to_i
    [1, 2, 3, #<A:0x2870cd0>, 4, 5]

    Regards,

    Sean
     
    Sean O'Halpin, Oct 19, 2005
    #6
  7. Hi,

    In message "Re: Array.sort when it's items are String inheritors with redefined <=> works like if not redefined"
    on Wed, 19 Oct 2005 23:06:04 +0900, James Edward Gray II <> writes:

    |> a = ['1', '10', '5']
    |> puts a.sort_by{|x|x.to_u}.join(',')
    |
    |I believe that second line is supposed to read:
    |
    | puts a.sort_by{|x|x.to_i}.join(',')

    Oops, you're right. Thank you for correction.

    matz.
     
    Yukihiro Matsumoto, Oct 19, 2005
    #7
  8. MiG

    Ara.T.Howard Guest

    Re: Array.sort when it's items are String inheritors with redefined<=> works like if not redefined

    On Wed, 19 Oct 2005, Sean O'Halpin wrote:

    > On 10/19/05, Ara.T.Howard <> wrote:
    >> this avoids calling to_i, to_f, or whatever multiple times on the same object,
    >> which will occur if you use either a spacship (<=>) operator or sort_by
    >> approach.

    >
    > I thought sort_by was a packaged form of the Schwartzian transform, i.e.
    >
    > class A
    > def to_i
    > puts "in A.to_i"
    > 3
    > end
    > end
    >
    > p [1, 2, 3, 4, 5, A.new].sort_by {|x| x.to_i }
    > # is equivalent to
    > p [1, 2, 3, 4, 5, A.new].map{|x| [x.to_i, x]}.sort{|y, z| y[0] <=>
    > z[0]}.map{|x| x[1]}
    >
    > __END__
    > in A.to_i
    > [1, 2, 3, #<A:0x2870f28>, 4, 5]
    > in A.to_i
    > [1, 2, 3, #<A:0x2870cd0>, 4, 5]


    you are quite right sean - i guess that only applies to the op's original

    a.to_i <=> b.to_i

    where you could end up doing that more than once.

    regards.

    -a
    --
    ===============================================================================
    | email :: ara [dot] t [dot] howard [at] noaa [dot] gov
    | phone :: 303.497.6469
    | anything that contradicts experience and logic should be abandoned.
    | -- h.h. the 14th dalai lama
    ===============================================================================
     
    Ara.T.Howard, Oct 19, 2005
    #8
  9. I think you have a bug here, Ara:

    On 10/19/05, Ara.T.Howard <> wrote:
    >
    > harp:~ > cat a.rb
    > class Array
    > def sort_as!
    > map!{|elem| yield elem}
    > sort!
    > self
    > end
    > def sort_as
    > dup.sort! #???
    > end


    How about:

    def sort_as(&block)
    dup.sort_as!(&block)
    end

    > end
    >
    >
    > a =3D %w( 1 10 5 )
    >
    > p a.sort_as{|s| Integer s}
    > p a.sort_as{|s| Float s}
    > p a.sort_as{|s| s.reverse }
    >
    > a.sort_as!{|s| Integer s}
    > p a


    C:\_Ryan\ruby>ruby a.rb
    [1, 5, 10]
    [1.0, 5.0, 10.0]
    ["01", "1", "5"]
    [1, 5, 10]

    I'm not sure it is worth adding such a method, when a map{}.sort would
    do the same thing, and is more explicit. Plus we have sort_by to solve
    the OP's problem.

    Ryan
     
    Ryan Leavengood, Oct 19, 2005
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. sajuptpm
    Replies:
    7
    Views:
    832
    Terry Reedy
    Sep 8, 2010
  2. Navin
    Replies:
    1
    Views:
    762
    Ken Schaefer
    Sep 9, 2003
  3. GIMME
    Replies:
    5
    Views:
    200
    Thomas 'PointedEars' Lahn
    Jul 26, 2004
  4. joe
    Replies:
    2
    Views:
    123
  5. juglesh
    Replies:
    7
    Views:
    192
    Thomas 'PointedEars' Lahn
    May 8, 2005
Loading...

Share This Page