why doesn't sort properly?

F

filippo2991

I have the following piece of code that doesn't work properly.

<MYFILE> is .csv file (semicolon delimited fields).
I split each row into an array
I put a reference of each array into another array
I want to order data by the seventh column of .csv data. The order
(ascending/descending) depends on $order value. My code (below) doesnt
work. Why?

is there a less redundant (and working) way to do that?

my @lines;

while (<MYFILE>) {
push @lines, [split /;/];
}


if ($order == 1) {

sub by_seventh {
$a->[6] <=> $b->[6];
}

foreach (sort by_seventh @lines) {

print DATA_SORT join (';', @{$_});
}

} else {

sub by_seventh {
$b->[6] <=> $a->[6];
}
foreach (sort by_seventh @lines) {

print DATA_SORT join (';', @{$_});
}

}
 
A

A. Sinan Unur

(e-mail address removed) wrote in @g44g2000cwa.googlegroups.com:
I have the following piece of code that doesn't work properly.

<MYFILE> is .csv file (semicolon delimited fields).

Of course, to be able to check why your code "doesn't work", we would have
to see some data.

....

Or, maybe not:
if ($order == 1) {

sub by_seventh {
....
} else {

sub by_seventh { ....
}

You should always, yes always,

use strict;

and

use warnings;

You should also read the documentation for the functions you are using:

perldoc -f sort


if( $order == 1 ) {
@lines = sort { $a->[6] <=> $b->[6] };
} else {
@lines = sort { $b->[6] <=> $a->[6] };
}

Have you seen the posting guidelines for this group?

Sinan
 
C

Ch Lamprecht

I have the following piece of code that doesn't work properly.

<MYFILE> is .csv file (semicolon delimited fields).
I split each row into an array
I put a reference of each array into another array
I want to order data by the seventh column of .csv data. The order
(ascending/descending) depends on $order value. My code (below) doesnt
work. Why?

is there a less redundant (and working) way to do that?

my @lines;

while (<MYFILE>) {
push @lines, [split /;/];
}


if ($order == 1) {

sub by_seventh {
$a->[6] <=> $b->[6];
}

foreach (sort by_seventh @lines) {

print DATA_SORT join (';', @{$_});
}

} else {

sub by_seventh {
$b->[6] <=> $a->[6];
}
foreach (sort by_seventh @lines) {

print DATA_SORT join (';', @{$_});
}

}
Hi,
you could check $order in function by_seventh like that:


use strict;
use warnings;

my @lines;

while (<DATA>) {
push @lines, [split /;/];
}
my $order = 1;

sub by_seventh {
my $test = $a->[6] <=> $b->[6];
return $order? $test:-$test;
}

foreach (sort by_seventh @lines) {
print join (';', @{$_});
}

__DATA__
; ; ; ; ; ;9;
; ; ; ; ; ;2;
; ; ; ; ; ;4;

hth Christoph
 
U

Uri Guttman

CL> you could check $order in function by_seventh like that:

CL> my $order = 1;

CL> sub by_seventh {
CL> my $test = $a->[6] <=> $b->[6];
CL> return $order? $test:-$test;
CL> }

CL> foreach (sort by_seventh @lines) {
CL> print join (';', @{$_});
CL> }

that is a very poor idea. it makes it hard to use that comparison sub
elsewhere as $order is a file lexical here. using a global just for this
is poor design. also it adds unneeded overhead to each comparison there
are so many other ways to do it. you could reverse the resulting sorted
array if the data set is small enough. you could declare two compare
subs, one for each direction.

uri
 
P

Paul Lalli

A. Sinan Unur said:
if( $order == 1 ) {
@lines = sort { $a->[6] <=> $b->[6] };
} else {
@lines = sort { $b->[6] <=> $a->[6] };
}

Hmmm....
$ perl -e'my @lines = (5, 3, 1, 2, 4); @lines = sort { $a <=> $b };'
syntax error at -e line 1, near "};"
Execution of -e aborted due to compilation errors.

ITYM
@lines = sort { $a->[6] <=> $b->[6] } @lines;
and
@lines = sort { $b->[6] <=> $a->[6] } @lines;

Paul Lalli
 
A

A. Sinan Unur

A. Sinan Unur said:
if( $order == 1 ) {
@lines = sort { $a->[6] <=> $b->[6] };
} else {
@lines = sort { $b->[6] <=> $a->[6] };
}

Hmmm....
$ perl -e'my @lines = (5, 3, 1, 2, 4); @lines = sort { $a <=> $b };'
....

ITYM
@lines = sort { $a->[6] <=> $b->[6] } @lines;

Absolutely. Hasty post. Thanks for the correction.


Sinan
 
X

Xicheng

I have the following piece of code that doesn't work properly.

<MYFILE> is .csv file (semicolon delimited fields).
I split each row into an array
I put a reference of each array into another array
I want to order data by the seventh column of .csv data. The order
(ascending/descending) depends on $order value. My code (below) doesnt
work. Why?

is there a less redundant (and working) way to do that?
hash may be better to solve this kind of problems, say...
============
my %h=();
while(<DATA>) {
my $k = (split/;/)[6];
$h{$k} = $_;
}
foreach my $k(sort {$a <=> $b} keys %h) {
print $h{$k};
}
#(code untested)
============
or on the command line:
perl -e '$h{(split/;/)[6]}=$_ while<>;print $h{$_}for(sort{$a<=>$b}
keys %h)' MY_FILE
or:
sort -t';' -nk7 MY_FILE

Xicheng
my @lines;

while (<MYFILE>) {
push @lines, [split /;/];
}


if ($order == 1) {

sub by_seventh {
$a->[6] <=> $b->[6];
}

foreach (sort by_seventh @lines) {

print DATA_SORT join (';', @{$_});
}

} else {

sub by_seventh {
$b->[6] <=> $a->[6];
}
foreach (sort by_seventh @lines) {

print DATA_SORT join (';', @{$_});
}

}
 
F

filippo2991

Thanks Xicheng for your proposal, the problem is that the numbers $k
are not unique, if I use them as keys for an hash I lose data.

fields #6 are numbers like that:

34.104
34.646
24.124
17.136
16.876
20.432
20.455
34.05

if I sort them replacing the if block

if( $order == 1 ) {
@lines = sort { $a->[6] <=> $b->[6] };
} else {
@lines = sort { $b->[6] <=> $a->[6] };
}

with

@lines = sort { $a->[6] <=> $b->[6] };

the routine works fine. But I want to choose ascending/descending.

Any idea?
Thanks




I want to sort the records by these numbers
 
A

Anno Siegel

Uri Guttman said:
CL> you could check $order in function by_seventh like that:

CL> my $order = 1;

CL> sub by_seventh {
CL> my $test = $a->[6] <=> $b->[6];
CL> return $order? $test:-$test;
CL> }

CL> foreach (sort by_seventh @lines) {
CL> print join (';', @{$_});
CL> }

that is a very poor idea. it makes it hard to use that comparison sub
elsewhere as $order is a file lexical here. using a global just for this
is poor design. also it adds unneeded overhead to each comparison there
are so many other ways to do it. you could reverse the resulting sorted
array if the data set is small enough.

Ah, but the *bigger* the data set is, the less will the linear reverse
be noticeable against the n log n sort.
you could declare two compare
subs, one for each direction.

Just for fun, I tried this:

my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
my @sorted = sort { $$ar <=> $$br } @line;

but in the sort block $$ar and $$br come up undefined. I don't quite
understand that. It must be something about the way $a and $b are
aliased to the list elements.

Anno
 
C

Ch Lamprecht

Anno said:
Uri Guttman said:
"CL" == Ch Lamprecht <[email protected]> writes:

CL> you could check $order in function by_seventh like that:

CL> my $order = 1;

CL> sub by_seventh {
CL> my $test = $a->[6] <=> $b->[6];
CL> return $order? $test:-$test;
CL> }

CL> foreach (sort by_seventh @lines) {
CL> print join (';', @{$_});
CL> }

that is a very poor idea. it makes it hard to use that comparison sub
elsewhere as $order is a file lexical here. using a global just for this
is poor design. also it adds unneeded overhead to each comparison there
are so many other ways to do it. you could reverse the resulting sorted
array if the data set is small enough.


Ah, but the *bigger* the data set is, the less will the linear reverse
be noticeable against the n log n sort.

you could declare two compare
subs, one for each direction.


Just for fun, I tried this:

my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
my @sorted = sort { $$ar <=> $$br } @line;

but in the sort block $$ar and $$br come up undefined. I don't quite
understand that. It must be something about the way $a and $b are
aliased to the list elements.

Anno

I don't think, ' ? : ' will return a list.

Christoph
 
C

Ch Lamprecht

Ch said:
Anno said:
Uri Guttman said:
CL> you could check $order in function by_seventh like that:

CL> my $order = 1;

CL> sub by_seventh {
CL> my $test = $a->[6] <=> $b->[6];
CL> return $order? $test:-$test;
CL> }

CL> foreach (sort by_seventh @lines) {
CL> print join (';', @{$_});
CL> }

that is a very poor idea. it makes it hard to use that comparison sub
elsewhere as $order is a file lexical here. using a global just for this
is poor design. also it adds unneeded overhead to each comparison there
are so many other ways to do it. you could reverse the resulting sorted
array if the data set is small enough.



Ah, but the *bigger* the data set is, the less will the linear reverse
be noticeable against the n log n sort.

you could declare two compare
subs, one for each direction.



Just for fun, I tried this:

my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
my @sorted = sort { $$ar <=> $$br } @line;

but in the sort block $$ar and $$br come up undefined. I don't quite
understand that. It must be something about the way $a and $b are
aliased to the list elements.

Anno


I don't think, ' ? : ' will return a list.
sorry for that, I should have checked perlop first... ;)
 
P

Paul Lalli

I don't think, ' ? : ' will return a list.

No reason to guess. 1) Read the Documentation. 2) Try an example

perldoc perlop:
Conditional Operator

Ternary "?:" is the conditional operator, just as in C.
<...>
Scalar or list context propagates downward into the 2nd or
3rd argument, whichever is selected.

$a = $ok ? $b : $c; # get a scalar
@a = $ok ? @b : @c; # get an array
$a = $ok ? @b : @c; # oops, that's just a count!



$ perl -le'$x = 1; ($a, $b) = $x ? (1, 2) : (3, 4); print "A: $a, B:
$b"'
A: 1, B: 2
$ perl -le'$x = 0; ($a, $b) = $x ? (1, 2) : (3, 4); print "A: $a, B:
$b"'
A: 3, B: 4

Paul Lalli
 
A

Anno Siegel

Ch Lamprecht said:
Anno said:
Uri Guttman said:
CL> you could check $order in function by_seventh like that:

CL> my $order = 1;

CL> sub by_seventh {
CL> my $test = $a->[6] <=> $b->[6];
CL> return $order? $test:-$test;
CL> }

CL> foreach (sort by_seventh @lines) {
CL> print join (';', @{$_});
CL> }

that is a very poor idea. it makes it hard to use that comparison sub
elsewhere as $order is a file lexical here. using a global just for this
is poor design. also it adds unneeded overhead to each comparison there
are so many other ways to do it. you could reverse the resulting sorted
array if the data set is small enough.


Ah, but the *bigger* the data set is, the less will the linear reverse
be noticeable against the n log n sort.

you could declare two compare
subs, one for each direction.


Just for fun, I tried this:

my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
my @sorted = sort { $$ar <=> $$br } @line;

but in the sort block $$ar and $$br come up undefined. I don't quite
understand that. It must be something about the way $a and $b are
aliased to the list elements.

Anno

I don't think, ' ? : ' will return a list.

It does, test it.

for my $z ( 0, 1 ) {
my ( $x, $y) = $z ? qw( a b) : qw( b a);
print "$z: $x $y\n";
}

Anyway, that is only a method to set $ar and $br to the required
references and it could be done any other way. The question is why
the references don't de-reference to the values $a and $b have inside
the sort block.

Anno
 
P

Paul Lalli

Anno said:
Just for fun, I tried this:

my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
my @sorted = sort { $$ar <=> $$br } @line;

but in the sort block $$ar and $$br come up undefined. I don't quite
understand that. It must be something about the way $a and $b are
aliased to the list elements.

Indeed. It would seem that $a and $b are reassigned in each
"iteration" of the sort:
$ perl -le'
my $order = 1;
my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
print "A_REF: $ar, B_REF: $br";
my @sorted = sort { print "A: $ar - ".\$a.", B: $br - ".\$a; $$ar <=>
$$br } (5, 1, 4);
'
A_REF: SCALAR(0xfe648), B_REF: SCALAR(0xfe660)
A: SCALAR(0xfe648) - SCALAR(0xfe780), B: SCALAR(0xfe660) -
SCALAR(0xfe780)
A: SCALAR(0xfe648) - SCALAR(0xfe78c), B: SCALAR(0xfe660) -
SCALAR(0xfe78c)

I'm willing to bet the DWIM could be achieved by manipulating the
symbol table and using typeglobs.... but I don't want to go there.

Paul Lalli
 
P

Paul Lalli

Ch said:
Thanks, I noticed already ... that was too hasty.

Yeah, your retraction arrived at my server between the time I hit
"reply" and the time I hit "send". :)

Paul Lalli
 
A

Anno Siegel

Paul Lalli said:
Indeed. It would seem that $a and $b are reassigned in each
"iteration" of the sort:
$ perl -le'
my $order = 1;
my ( $ar, $br) = $order ? \ ( $a, $b) : \ ( $b, $a);
print "A_REF: $ar, B_REF: $br";
my @sorted = sort { print "A: $ar - ".\$a.", B: $br - ".\$a; $$ar <=>
$$br } (5, 1, 4);
'
A_REF: SCALAR(0xfe648), B_REF: SCALAR(0xfe660)
A: SCALAR(0xfe648) - SCALAR(0xfe780), B: SCALAR(0xfe660) -
SCALAR(0xfe780)
A: SCALAR(0xfe648) - SCALAR(0xfe78c), B: SCALAR(0xfe660) -
SCALAR(0xfe78c)

I'm willing to bet the DWIM could be achieved by manipulating the
symbol table and using typeglobs.... but I don't want to go there.

Ah, yes, that works:

my ( $ag, $bg) = $order? ( *a, *b) : ( *b, *a);
my @sorted = sort { $$ag <=> $$bg } @line;

But ugh!

Anno
 
C

Ch Lamprecht

Anno said:
Ah, yes, that works:

my ( $ag, $bg) = $order? ( *a, *b) : ( *b, *a);
my @sorted = sort { $$ag <=> $$bg } @line;

But ugh!

Anno
Hi, what about this (might be trivial?):

use warnings;
#use strict;
use Data::Dumper;
my @line = (8,2,5,4,3,6,7);
my $order = 1;
my ($ar, $br) = $order ? ('a','b') : ('b','a');
my @sorted = sort { ${$ar} <=> ${$br} } @line;
print Dumper \@sorted;

Christoph
 
A

Anno Siegel

Ch Lamprecht said:
Hi, what about this (might be trivial?):

use warnings;
#use strict;
use Data::Dumper;
my @line = (8,2,5,4,3,6,7);
my $order = 1;
my ($ar, $br) = $order ? ('a','b') : ('b','a');
my @sorted = sort { ${$ar} <=> ${$br} } @line;
print Dumper \@sorted;

Symrefs? Double ugh! Globs are at least somewhat respectable :)

Anno
 
P

Paul Lalli

Ch said:
use warnings;
#use strict;
use Data::Dumper;
my @line = (8,2,5,4,3,6,7);
my $order = 1;
my ($ar, $br) = $order ? ('a','b') : ('b','a');
my @sorted = sort { ${$ar} <=> ${$br} } @line;
print Dumper \@sorted;

Gah. Don't comment out 'use strict'. That's just a bad idea. If you
*need* to turn it off, turn off only the part of it you need to turn
off, and do so in the smallest scope possible:

#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my @line = (8,2,5,4,3,6,7);
my $order = 0;
my ($ar, $br) = $order ? ('a','b') : ('b','a');
my @sorted = sort using_symrefs @line;
print Dumper \@sorted;

sub using_symrefs {
no strict 'refs';
${$ar} <=> ${$br};
}

Now, whether that's better or worse than using typeglobs, it's hard to
say...

Paul Lalli
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top