views:

91

answers:

5

This make sense for things like :

irb(main):001:0> ["b", "aa", "d", "dd"].sort
=> ["aa", "b", "d", "dd"]

But doesn't for :

irb(main):002:0> ("B".."AA").each{ |x| print "#{x}," }
=> "B".."AA"

should produce : B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,=> "B".."AA" but "B" > "AA" => true

Unlike "B".."BA" ("B" > "BA" => false) :

irb(main):003:0> ("B".."BA").each{ |x| print "#{x}," }
B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,=> "B".."BA"

Any advice to make "b".."aa" work as expected in ruby ?

I use

  • irb 0.9.5(05/04/13) ruby 1.8.7
  • (2009-06-12 patchlevel 174) [i486-linux]
  • Linux 2.6.31-19-generic #56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010 i686 GNU/Linux
A: 

This was reported as a bug in Ruby here. The results depend on which version of Ruby you are running. There is a difference between versions 1.8.6 and 1.9.1.

Mark Byers
No, it's not the sort order: he's trying to do "B".."AA", which Ruby (for him, and me) evaluates to []. You can sort [] any way you like but it's still []. :-) The docs seem to indicate that `succ` is used, and that's defined for "B", "C", ..., "Z", "AA", so it should work as he expects.
Ken
Ah, missed that, thanks. Looks like a bug in some versions of Ruby.
Mark Byers
Updated answer.
Mark Byers
It's actually fixed in ruby 1.8.7 (2009-06-12 patchlevel 174)
Chris McCauley
Yep, "b".."ba" works but not "b".."ab". It also doesn't work in ruby 1.9.1p376 (2009-12-07 revision 26041) [i486-linux]
Chris McCauley
The bug is marked as rejected. I don't think they want to fix it.
Mark Byers
A: 

Any advice to make "b".."aa" work as expected in ruby ?

This DOESN'T work in ruby 1.8.7 (2009-06-12 patchlevel 174) nor in ruby 1.9.1p376 (2009-12-07 revision 26041) [i486-linux]


"b".."ba" does work...

irb(main):001:0> ("b".."ba").each {|x| print "#{x} "} b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba => "b".."ba"

Chris McCauley
Quoting isn't the issue, since there's no interpolation here. Perhaps it's a version issue: on 1.8.7 here your example prints "b".."aa" (the range itself, which has no elements). What version of Ruby are you using?
Ken
ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux] : You're right - it didn't work in irb because I had a spare " so it didn't work - I retyped and both "b".."aa" and 'b'..'aa' work
Chris McCauley
ruby 1.8.7 (2008-08-11 patchlevel 72) -- I must have just missed it!
Ken
Grand! Glad to have helped eventually!
Chris McCauley
this work in deed for 'b'..'ba' but not for 'b'..'aa'
Doud
A: 

It looks like Ruby is using succ, but first it checks for end>=start, and since that is false here, it doesn't even try.

Admittedly String#succ is a weird beast here: a string's successor isn't always greater than the string, using its own comparison methods. So I'm not sure if this is technically a bug or not. It does look pretty confusing if you don't know this undocumented check, though.

Then again, judging by some of the other answers here, it looks like it does work as you expect in some versions of Ruby, so maybe it was fixed in 1.9?

Ken
Doesn't work in ruby 1.9.1p376 (2009-12-07 revision 26041) [i486-linux]
Chris McCauley
+1  A: 

The best way to do this is to subclass String and redefine the comparison operator to meet your needs. Then use your new class to make the range.

class MyString < String
  def initialize str=""
    super str
  end

  def <=>(other)
    length_cmp = self.length <=> other.length
    return length_cmp unless length_cmp == 0
    super other
  end
end

Now you can ensure that a column appears before another.

"b" < "aa" #=> false
MyString.new("b") < MyString.new("aa") #=> true

N.B.: Only the string on the left side of any comparison operator needs to be of class MyString:

MyString.new("b") < "aa" #=> true
"aa" > MyString.new("b") #=> false
EmFi
A: 

It is true that in class String, <=> and String#succ are not completely harmonized


I suppose it would be nice if for each a, b where b eventually is produced from a.succ.succ..., it was also true that a <=> b returned -1. One reason this would be nice is that it is in fact precisely <=> and succ that are used to implement ranges in the first place. Consequently, as you have noted, a Ruby String range where .succ would eventually complete the expansion doesn't work because a <=> test contradicts it and terminates the loop.

So yes, the ordering, at least for String, that is defined by <=> doesn't match the #succ method ordering. This is one reason that some developers avoid using succ.

DigitalRoss