Sorting numbers as strings

Discussion in 'Ruby' started by Jack Bauer, May 18, 2009.

  1. Jack Bauer

    Jack Bauer Guest

    I'm trying to sort some strings containing numbers. The strings
    themselves can't be changed (they're items being pulled from a DB.) This
    is an example of some of the things I need to sort. First is how I
    wanted them sorted:

    FastEthernet0/1
    FastEthernet0/10
    FastEthernet0/11
    FastEthernet0/12
    FastEthernet0/13
    FastEthernet0/2
    FastEthernet0/3
    FastEthernet0/4
    FastEthernet0/5
    FastEthernet0/6
    FastEthernet0/7
    FastEthernet0/8
    FastEthernet0/9
    ...

    I need to get it like this:
    FastEthernet0/1
    FastEthernet0/2
    FastEthernet0/3
    FastEthernet0/4
    FastEthernet0/5
    FastEthernet0/6
    FastEthernet0/7
    FastEthernet0/8
    FastEthernet0/9
    FastEthernet0/10
    FastEthernet0/11
    FastEthernet0/12
    FastEthernet0/13

    Then they could turn into FastEthernet1/1, etc. Also, the name doesn't
    really matter as it could have FastEthernet, another name, or none at
    all and just be 0/1 or whatever.

    I'm guessing something like a regex to strip non-numbers so
    FastEthernet0/1 becomes 01 then sort numerically, but that wouldn't help
    if I have FastEthernet0/1, GigabitEthernet0/1, and FastEthernet0/2 since
    it would come out in an incorrect order (01, 01, 02 instead of 01, 02,
    01 because of the F and G alphabetical sort.)

    Any ideas of the best way to go about this?
    --
    Posted via http://www.ruby-forum.com/.
    Jack Bauer, May 18, 2009
    #1
    1. Advertising

  2. Jack Bauer

    Jack Bauer Guest

    Jack Bauer wrote:
    > First is how I wanted them sorted:




    Sorry I meant to say that the first version is how they're CURRENTLY
    being sorted and the second version is how I WANT them sorted.
    --
    Posted via http://www.ruby-forum.com/.
    Jack Bauer, May 18, 2009
    #2
    1. Advertising

  3. [Note: parts of this message were removed to make it a legal post.]

    Maybe something like this:

    irb> a = ["FastEthernet0/1", "FastEthernet0/10", "FastEthernet0/11",
    "FastEthernet0/12", "FastEthernet0/13", "FastEthernet0/2",
    "FastEthernet0/3", "FastEthernet0/4", "FastEthernet0/5", "FastEthernet0/6",
    "FastEthernet0/7", "FastEthernet0/8", "FastEthernet0/9"]
    irb> a.sort_by{|s| n,i = s.split('/'); [n, i.to_i]}
    => ["FastEthernet0/1", "FastEthernet0/2", "FastEthernet0/3",
    "FastEthernet0/4", "FastEthernet0/5", "FastEthernet0/6", "FastEthernet0/7",
    "FastEthernet0/8", "FastEthernet0/9", "FastEthernet0/10",
    "FastEthernet0/11", "FastEthernet0/12", "FastEthernet0/13"]


    On Mon, May 18, 2009 at 7:27 AM, Jack Bauer <> wrote:

    > Jack Bauer wrote:
    > > First is how I wanted them sorted:

    >
    >
    >
    > Sorry I meant to say that the first version is how they're CURRENTLY
    > being sorted and the second version is how I WANT them sorted.
    > --
    > Posted via http://www.ruby-forum.com/.
    >
    >
    Douglas Seifert, May 18, 2009
    #3
  4. 2009/5/18 Jack Bauer <>:
    > I'm trying to sort some strings containing numbers. The strings
    > themselves can't be changed (they're items being pulled from a DB.) This
    > is an example of some of the things I need to sort. First is how I
    > wanted them sorted:
    >
    > FastEthernet0/1
    > FastEthernet0/10
    > FastEthernet0/11
    > FastEthernet0/12
    > FastEthernet0/13
    > FastEthernet0/2
    > FastEthernet0/3
    > FastEthernet0/4
    > FastEthernet0/5
    > FastEthernet0/6
    > FastEthernet0/7
    > FastEthernet0/8
    > FastEthernet0/9
    > ...
    >
    > I need to get it like this:
    > FastEthernet0/1
    > FastEthernet0/2
    > FastEthernet0/3
    > FastEthernet0/4
    > FastEthernet0/5
    > FastEthernet0/6
    > FastEthernet0/7
    > FastEthernet0/8
    > FastEthernet0/9
    > FastEthernet0/10
    > FastEthernet0/11
    > FastEthernet0/12
    > FastEthernet0/13
    >
    > Then they could turn into FastEthernet1/1, etc. Also, the name doesn't
    > really matter as it could have FastEthernet, another name, or none at
    > all and just be 0/1 or whatever.
    >
    > I'm guessing something like a regex to strip non-numbers so
    > FastEthernet0/1 becomes 01 then sort numerically, but that wouldn't help
    > if I have FastEthernet0/1, GigabitEthernet0/1, and FastEthernet0/2 since
    > it would come out in an incorrect order (01, 01, 02 instead of 01, 02,
    > 01 because of the F and G alphabetical sort.)
    >
    > Any ideas of the best way to go about this?


    16:39:42 Temp$ ruby19 srt.rb
    FastEthernet0/1
    FastEthernet0/2
    FastEthernet0/3
    FastEthernet0/4
    FastEthernet0/5
    FastEthernet0/6
    FastEthernet0/7
    FastEthernet0/8
    FastEthernet0/9
    FastEthernet0/10
    FastEthernet0/11
    FastEthernet0/12
    FastEthernet0/13
    16:40:24 Temp$ cat srt.rb
    puts DATA.sort_by {|s| s.scan(/\d+/).map {|x| x.to_i} }
    __END__
    FastEthernet0/1
    FastEthernet0/10
    FastEthernet0/11
    FastEthernet0/12
    FastEthernet0/13
    FastEthernet0/2
    FastEthernet0/3
    FastEthernet0/4
    FastEthernet0/5
    FastEthernet0/6
    FastEthernet0/7
    FastEthernet0/8
    FastEthernet0/9
    16:40:28 Temp$

    Kind regards

    robert


    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, May 18, 2009
    #4
  5. Jack Bauer

    Jack Bauer Guest

    Douglas Seifert wrote:
    > Maybe something like this:
    > irb> a.sort_by{|s| n,i = s.split('/'); [n, i.to_i]}


    Thanks to both of you guys. The one I quoted above looks similar to what
    I was doing, I just thought it wasn't working because I'm using
    pagination and it's ordering each page rather than the whole result and
    then paginating.
    --
    Posted via http://www.ruby-forum.com/.
    Jack Bauer, May 18, 2009
    #5
  6. On May 18, 2009, at 10:40 AM, Robert Klemme wrote:

    > 2009/5/18 Jack Bauer <>:
    >> I'm trying to sort some strings containing numbers. The strings
    >> themselves can't be changed (they're items being pulled from a DB.)
    >> This
    >> is an example of some of the things I need to sort. First is how I
    >> wanted them sorted:
    >>
    >> FastEthernet0/1
    >> FastEthernet0/10
    >> FastEthernet0/11
    >> FastEthernet0/12
    >> FastEthernet0/13
    >> FastEthernet0/2
    >> FastEthernet0/3
    >> FastEthernet0/4
    >> FastEthernet0/5
    >> FastEthernet0/6
    >> FastEthernet0/7
    >> FastEthernet0/8
    >> FastEthernet0/9
    >> ...
    >>
    >> I need to get it like this:
    >> FastEthernet0/1
    >> FastEthernet0/2
    >> FastEthernet0/3
    >> FastEthernet0/4
    >> FastEthernet0/5
    >> FastEthernet0/6
    >> FastEthernet0/7
    >> FastEthernet0/8
    >> FastEthernet0/9
    >> FastEthernet0/10
    >> FastEthernet0/11
    >> FastEthernet0/12
    >> FastEthernet0/13
    >>
    >> Then they could turn into FastEthernet1/1, etc. Also, the name
    >> doesn't
    >> really matter as it could have FastEthernet, another name, or none at
    >> all and just be 0/1 or whatever.
    >>
    >> I'm guessing something like a regex to strip non-numbers so
    >> FastEthernet0/1 becomes 01 then sort numerically, but that wouldn't
    >> help
    >> if I have FastEthernet0/1, GigabitEthernet0/1, and FastEthernet0/2
    >> since
    >> it would come out in an incorrect order (01, 01, 02 instead of 01,
    >> 02,
    >> 01 because of the F and G alphabetical sort.)
    >>
    >> Any ideas of the best way to go about this?

    >
    > 16:40:24 Temp$ cat srt.rb
    > puts DATA.sort_by {|s| s.scan(/\d+/).map {|x| x.to_i} }

    <snip/>
    >
    > Kind regards
    >
    > robert
    > --
    > remember.guy do |as, often| as.you_can - without end
    > http://blog.rubybestpractices.com/


    If you do need to consider things like "FastEthernet..." v.
    "GigabitEthernet...", then perhaps you want something like this which
    breaks the original string into digits and non-digits and converts the
    digits to be integers.

    puts DATA.sort_by {|s| s.scan(/\d+|\D+/).map {|x| x =~ /\d+/ ?
    x.to_i : x } }

    -Rob

    Rob Biedenharn http://agileconsultingllc.com
    Rob Biedenharn, May 18, 2009
    #6
  7. On 18.05.2009 17:37, Rob Biedenharn wrote:

    > If you do need to consider things like "FastEthernet..." v.
    > "GigabitEthernet...", then perhaps you want something like this which
    > breaks the original string into digits and non-digits and converts the
    > digits to be integers.
    >
    > puts DATA.sort_by {|s| s.scan(/\d+|\D+/).map {|x| x =~ /\d+/ ?
    > x.to_i : x } }


    Good point! Here's an interesting variant:

    puts DATA.sort_by {|s| s.scan(/(\d+)|\D+/).map { $1 ? $1.to_i : $& } }

    Kind regards

    robert


    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, May 18, 2009
    #7
  8. On May 18, 2009, at 1:01 PM, Robert Klemme wrote:

    > On 18.05.2009 17:37, Rob Biedenharn wrote:
    >
    >> If you do need to consider things like "FastEthernet..." v.
    >> "GigabitEthernet...", then perhaps you want something like this
    >> which breaks the original string into digits and non-digits and
    >> converts the digits to be integers.
    >> puts DATA.sort_by {|s| s.scan(/\d+|\D+/).map {|x| x =~ /\d+/ ?
    >> x.to_i : x } }

    >
    > Good point! Here's an interesting variant:
    >
    > puts DATA.sort_by {|s| s.scan(/(\d+)|\D+/).map { $1 ? $1.to_i : $& } }
    >
    > Kind regards
    >
    > robert
    > --
    > remember.guy do |as, often| as.you_can - without end
    > http://blog.rubybestpractices.com/


    Interesting, but "wrong" in that it doesn't sort the way the OP wanted.
    Actually, since all the regexp applications have been applied by #scan
    before the #map happens, the values of $1 and $& are effectively
    constants and no sorting happens at all.

    However, that did inspire me to make my version a little better.

    puts DATA.sort_by {|s| s.scan(/(\d+)|(\D+)/).map {|(n,s)| s ||
    n.to_i } }

    I'd rather make the variables local than invoke the Perlish Regexp
    globals (even if they did were assigned in the block the way you
    expected). It could be even more readable if (n,s) were replaced with
    (digits,nondigits), but it looks OK to me with n and s.

    -Rob

    Rob Biedenharn http://agileconsultingllc.com
    Rob Biedenharn, May 19, 2009
    #8
  9. On 19.05.2009 02:55, Rob Biedenharn wrote:
    > On May 18, 2009, at 1:01 PM, Robert Klemme wrote:
    >
    >> On 18.05.2009 17:37, Rob Biedenharn wrote:
    >>
    >>> If you do need to consider things like "FastEthernet..." v.
    >>> "GigabitEthernet...", then perhaps you want something like this
    >>> which breaks the original string into digits and non-digits and
    >>> converts the digits to be integers.
    >>> puts DATA.sort_by {|s| s.scan(/\d+|\D+/).map {|x| x =~ /\d+/ ?
    >>> x.to_i : x } }

    >> Good point! Here's an interesting variant:
    >>
    >> puts DATA.sort_by {|s| s.scan(/(\d+)|\D+/).map { $1 ? $1.to_i : $& } }


    > Interesting, but "wrong" in that it doesn't sort the way the OP wanted.
    > Actually, since all the regexp applications have been applied by #scan
    > before the #map happens, the values of $1 and $& are effectively
    > constants and no sorting happens at all.


    Aaargh! Yes, you are completely right.

    > However, that did inspire me to make my version a little better.
    >
    > puts DATA.sort_by {|s| s.scan(/(\d+)|(\D+)/).map {|(n,s)| s ||
    > n.to_i } }
    >
    > I'd rather make the variables local than invoke the Perlish Regexp
    > globals (even if they did were assigned in the block the way you
    > expected). It could be even more readable if (n,s) were replaced with
    > (digits,nondigits), but it looks OK to me with n and s.


    Absolutely agree, I try to use local variables whenever possible.
    Although I recently learned that $1 etc. are local to the current stack
    frame! I did not knew that before and it certainly makes their use a
    lot safer.

    Thanks for catching my mistake!

    Kind regards

    robert

    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, May 19, 2009
    #9
  10. Lars Christensen, May 19, 2009
    #10
  11. Jack Bauer

    Jack Bauer Guest

    You guys are great. I went with Bob's (pick one, hah) since it ended up
    being moderately faster than the method I was originally using.
    --
    Posted via http://www.ruby-forum.com/.
    Jack Bauer, May 19, 2009
    #11
  12. On Tue, May 19, 2009 at 5:24 PM, Jack Bauer <> wrote:
    > You guys are great. I went with Bob's (pick one, hah) since it ended up
    > being moderately faster than the method I was originally using.
    > --


    Here is a variant similar to the other solutions you already got:

    puts arr.sort_by {|s| s.split(/(\d+)/).each_with_index.map {|x,i|
    i.odd? ? x.to_i : x }}

    Works with Ruby 1.9.

    /Johan Holmberg
    Johan Holmberg, May 19, 2009
    #12
  13. On May 19, 2009, at 5:51 PM, Johan Holmberg wrote:
    > On Tue, May 19, 2009 at 5:24 PM, Jack Bauer
    > <> wrote:
    >> You guys are great. I went with Bob's (pick one, hah) since it
    >> ended up
    >> being moderately faster than the method I was originally using.
    >> --

    >
    > Here is a variant similar to the other solutions you already got:
    >
    > puts arr.sort_by {|s| s.split(/(\d+)/).each_with_index.map {|x,i|
    > i.odd? ? x.to_i : x }}
    >
    > Works with Ruby 1.9.
    >
    > /Johan Holmberg



    Are you trying to say that you believe some part of a solution that
    Jack already accepted won't work in Ruby 1.9? (Because they should
    work fine.)

    -Rob

    Rob Biedenharn http://agileconsultingllc.com
    Rob Biedenharn, May 20, 2009
    #13
  14. On Wed, May 20, 2009 at 6:24 AM, Rob Biedenharn
    <> wrote:
    > On May 19, 2009, at 5:51 PM, Johan Holmberg wrote:
    >>
    >> On Tue, May 19, 2009 at 5:24 PM, Jack Bauer <>
    >> wrote:
    >>>
    >>> You guys are great. I went with Bob's (pick one, hah) since it ended up
    >>> being moderately faster than the method I was originally using.
    >>> --

    >>
    >> Here is a variant similar to the other solutions you already got:
    >>
    >> =A0 puts arr.sort_by {|s| s.split(/(\d+)/).each_with_index.map {|x,i|
    >> i.odd? ? x.to_i : x }}
    >>
    >> Works with Ruby 1.9.
    >>
    >> /Johan Holmberg

    >
    >
    > Are you trying to say that you believe some part of a solution that Jack
    > already accepted won't work in Ruby 1.9? (Because they should work fine.)
    >
    > -Rob
    >


    No, not at all. I tried to say that I believed *my* variant only
    worked in 1.9 (and maybe 1.8.7). (I should have said "Only works with
    Ruby 1.9").

    Sorry for the misunderstanding.

    /Johan Holmberg
    Johan Holmberg, May 20, 2009
    #14
    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. Carsten Zerbst
    Replies:
    6
    Views:
    6,803
    Roedy Green
    Aug 14, 2003
  2. Mars

    Sorting of numbers or strings.

    Mars, Mar 2, 2005, in forum: C Programming
    Replies:
    3
    Views:
    342
    Eric Sosman
    Mar 2, 2005
  3. Replies:
    6
    Views:
    308
    Amit Khemka
    Sep 25, 2007
  4. Delaney, Timothy (Tim)

    RE: sorting a list numbers stored as strings

    Delaney, Timothy (Tim), Sep 25, 2007, in forum: Python
    Replies:
    4
    Views:
    412
  5. one man army

    Numbers to strings to numbers again

    one man army, Dec 28, 2005, in forum: Javascript
    Replies:
    6
    Views:
    140
    one man army
    Dec 30, 2005
Loading...

Share This Page