Sort problem

Hi,

I have a file with data like this:

ina1
ina15
inb12
ina9
inc10
inda12
inef14
inab14
inzz8
inzz80
inzz9

etc.

I want to sort it alphabetically on the first part of the string, then
numerically on the second part.

The sorted file should look like this:

ina1
ina9
ina15
inab14
inb12
inc10
inda12
inef14
inzz8
inzz9
inzz80

However, I can’t seem to write the Ruby code to do this properly.

Having written the Perl to do this kind of thing many times before, I
set about writing it in Perl and sure enough, the Perl version
works. The Ruby code is so analagous that I just can’t see where I’m
going wrong.

Here are the two versions:

Perl

···

undef $/;
@foo = sort { ($c,$d) = ($a =~ /^([a-z]+)([0-9]+)$/);
($e,$f) = ($b =~ /^([a-z]+)([0-9]+)$/);
$c cmp $e or $d <=> $f} split(/\n/, <>);
print join “\n”, @foo

Ruby

$/ = nil
foo = gets.split("\n")
r = /^([a-z]+)([0-9]+)$/
foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
e,f = r.match(b)[1,2]
c <=> e or d.to_i <=> f.to_i}
puts foo

The Ruby version outputs this:

ina1
ina15
ina9
inab14
inb12
inc10
inda12
inef14
inzz80
inzz8
inzz9

which shows that the numeric comparison on the second part of the
string is clearly not working, leading me to suspect that the <=>
operator doesn’t work with objects of type Fixnum, but according to
the docs, it gets mixed in from Comparable.

So, what’s wrong with the Ruby code? This is Ruby 1.6.7, in case that
matters.

Ian

Ian Macdonald | Somewhere in Tenafly, New Jersey, a
ian@caliban.org | chiropractor is viewing “Leave it to
> Beaver”!
>
>

Hello –

···

On Wed, 7 Aug 2002, Ian Macdonald wrote:

Hi,

I have a file with data like this:

ina1
ina15
inb12
ina9
inc10
inda12
inef14
inab14
inzz8
inzz80
inzz9

etc.

I want to sort it alphabetically on the first part of the string, then
numerically on the second part.
$/ = nil
foo = gets.split("\n")
r = /^([a-z]+)([0-9]+)$/
foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
e,f = r.match(b)[1,2]
c <=> e or d.to_i <=> f.to_i}
puts foo

[doesn’t work]

It’s because <=> returns -1, 0, or 1, all of which are true in
Ruby.

You could drop in this last line of the sort:

(c == e) and (d.to_i <=> f.to_i) or (c <=> e)}

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Hi,

“Ian Macdonald” ian@caliban.org wrote in message
news:20020806235442.GA17078@caliban.org

Hi,

I have a file with data like this:

ina1
ina15
inb12
ina9
inc10
inda12
inef14
inab14
inzz8
inzz80
inzz9

etc.

I want to sort it alphabetically on the first part of the string, then
numerically on the second part.

Ruby

$/ = nil
foo = gets.split("\n")
r = /^([a-z]+)([0-9]+)$/
foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
e,f = r.match(b)[1,2]
c <=> e or d.to_i <=> f.to_i}
puts foo

So, what’s wrong with the Ruby code? This is Ruby 1.6.7, in case that
matters.

Try this:

$/ = nil
foo = gets.split("\n")
r = /^([a-z]+)([0-9]+)$/
foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
e,f = r.match(b)[1,2]
(c <=> e).nonzero? || d.to_i <=> f.to_i}
puts foo

Ian

Ian Macdonald | Somewhere in Tenafly, New Jersey, a
ian@caliban.org | chiropractor is viewing “Leave it to
> Beaver”!
>
>

Park Heesob

Hi,

Ian Macdonald ian@caliban.org writes:

:Ruby
:----
:
:$/ = nil
:foo = gets.split("\n")
:r = /^([a-z]+)([0-9]+)$/
:foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
: e,f = r.match(b)[1,2]
: c <=> e or d.to_i <=> f.to_i}
:puts foo

How about Array#<=> ?

[c, d.to_i] <=> [e, f.to_i]

···


eban

Hi –

···

On Wed, 7 Aug 2002, Park Heesob wrote:

$/ = nil
foo = gets.split("\n")
r = /^([a-z]+)([0-9]+)$/
foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
e,f = r.match(b)[1,2]
(c <=> e).nonzero? || d.to_i <=> f.to_i}
puts foo

It’s odd (to me) that #nonzero? doesn’t return true or false, a fact I
wasn’t aware of until looking at this (and wondering why it worked :slight_smile:

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

$/ = nil
foo = gets.split("\n")
r = /^([a-z]+)([0-9]+)$/
foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
e,f = r.match(b)[1,2]
c <=> e or d.to_i <=> f.to_i}
puts foo

[doesn’t work]

It’s because <=> returns -1, 0, or 1, all of which are true in
Ruby.

And, of course, I know about those values not returning truth in Ruby,
but it continues to bite me when I’m least expecting it.

This is one of those few areas where I think the Perl way actually
makes more sense.

You could drop in this last line of the sort:

(c == e) and (d.to_i <=> f.to_i) or (c <=> e)}

Thanks.

Ian

···

On Wed 07 Aug 2002 at 09:22:24 +0900, David Alan Black wrote:

Ian Macdonald | No house should ever be on any hill or on
ian@caliban.org | anything. It should be of the hill,
> belonging to it. – Frank Lloyd Wright
>
>

Ah, very elegant indeed! Thanks for this idiom.

Ian

···

On Wed 07 Aug 2002 at 14:31:47 +0900, WATANABE Hirofumi wrote:

Ian Macdonald ian@caliban.org writes:

:Ruby
:----
:
:$/ = nil
:foo = gets.split("\n")
:r = /^([a-z]+)([0-9]+)$/
:foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
: e,f = r.match(b)[1,2]
: c <=> e or d.to_i <=> f.to_i}
:puts foo

How about Array#<=> ?

[c, d.to_i] <=> [e, f.to_i]


Ian Macdonald | Collaboration, n.: A literary partnership
ian@caliban.org | based on the false assumption that the
> other fellow can spell.
>
>

And Ruby 1.7 has Array#sort_by.

foo = foo.sort_by { |i|
e,f = r.match(a)[1,2]
[e, f.to_i]
}

By the way, this example reminds me.
I would sometimes like to modifiy an array in a method chain.
So, I wrote Array#sub_at and Array#sub. With this method, above can be
rewritten as follows:

foo = foo.sort_by { |i| r.match(a)[1,2].sub_at(1) { |f| f.to_i } }

Or the original problem [ruby-talk:46468] can be answered as:

% cat ruby-talk:46468.rb
require "arraysub"
puts ARGF.readlines.sort_by { |line|
line.scan(/\D+|\d+/).sub(/\d/) { |f| f.to_i }
}

% cat ruby-talk:46468.dat
ina1
ina15
inb12
ina9
inc10
inda12
inef14
inab14
inzz8
inzz80
inzz9

% ruby17 ruby-talk:46468.rb ruby-talk:46468.dat
ina1
ina9
ina15
inab14
inb12
inc10
inda12
inef14
inzz8
inzz9
inzz80

I’ve put arraysub.rb at
http://www.notwork.org/~gotoken/ruby/p/as-is/arraysub.rb

– Gotoken

···

At Wed, 7 Aug 2002 14:31:47 +0900, WATANABE Hirofumi wrote:

:foo = foo.sort {|a,b| c,d = r.match(a)[1,2]
: e,f = r.match(b)[1,2]
: c <=> e or d.to_i <=> f.to_i}
:puts foo

How about Array#<=> ?

[c, d.to_i] <=> [e, f.to_i]

Hi –

···

On Thu, 8 Aug 2002, GOTO Kentaro wrote:

And Ruby 1.7 has Array#sort_by.

foo = foo.sort_by { |i|
e,f = r.match(a)[1,2]
foo = foo.sort_by { |i| r.match(a)[1,2].sub_at(1) { |f| f.to_i } }

s/|i|/|a|/g # :slight_smile:

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

puts "Looks like we’re becoming a forum"
puts "where we can’t talk at all unless"
puts "it’s in code or codelike form."
puts “:)”

puts “Cheers,\nHal” # Or perhaps Hal.cheers!

···

----- Original Message -----
From: “David Alan Black” dblack@candle.superlink.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, August 07, 2002 12:06 PM
Subject: Re: sort problem

s/|i|/|a|/g # :slight_smile:

=begin

From: “David Alan Black” dblack@candle.superlink.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org

s/|i|/|a|/g # :slight_smile:

puts "Looks like we’re becoming a forum"
puts "where we can’t talk at all unless"
puts "it’s in code or codelike form."
puts “:)”

puts “Cheers,\nHal” # Or perhaps Hal.cheers!
=end

%(

Oops! Thank you David for correcting.
And for cheerful comment, Hal :smiley:

– Gotoken
;).

display

···

At Thu, 8 Aug 2002 02:10:01 +0900, Hal E. Fulton hal9000@hypermetrics.com wrote: