Front page | perl.beginners |
Postings from April 2003
Re: multiple sorts
Thread Previous
|
Thread Next
From:
Rob Dixon
Date:
April 27, 2003 04:49
Subject:
Re: multiple sorts
Message ID:
20030427114920.30143.qmail@onion.perl.org
Hi Kevin.
Kevin Pfeiffer wrote:
> Hi,
>
> Following recommendations in the Cookbook,etc., I have an array of arrays of
> columns of tabular data on which I do a primary sort and then a secondary
> sort, somewhat like this:
>
> @sortedAoA = sort { $a->[name] cmp $b->[name]
> ||
> $b->[age] <=> $b->[age] } @AoA;
>
> (I hope my simplification didn't introduce any errors here)
I think you have an array of hashes, where each element
of the array (@AoH?) is an anonymous hash reference. So
I guess that should be
@sortedAoH = sort {
$a->{name} cmp $b->{name} ||
$b->{age} <=> $b->{age}
} @AoH
> This seemed fine and is simple, so far, but I am sorting on columns
> containing data that could be numbers or letters, so I then needed to
> account for things like 'if($col_is_number)' to choose the correct
> operator; for two sorts this gave me four variations (not counting
> 'reverse').
You want to sort data stringwise unless it is all numeric, when it should
be sorted numerically?
> What if I want the option now of sorting on three or more columns? This
> seems to become rather convoluted after a while and my sort subroutine is
> 70 lines long (I'm also doing transliteration of "รค" to "ae" for the sort,
> etc., which will be the subject of a future sort question).
>
> Instead of doing these 'sorts within sorts' couldn't I set up multiple,
> repeated sorts for each column, working backwards? So if I want to sort on
> country, then city, then name, I first do a sort on name, save results to
> array, then do a sort on city using this newly-sorted array, save, and then
> do a final sort on country.
>
> This may take longer, but I'm dealing with perhaps at most a couple three
> hundred lines of text, and it would easily let me set up a loop which gets
> the column number to sort on from user(be it 2, 3, or more) and then run
> the sorting loop as described.
>
I'm afraid you can't in general, because you need a sort routine that
guarantees that data with equal sort keys stay in the same order. The
Perl 'sort' function doesn't do this.
The obvious solution is to provide a subroutine which will compare a
pair of anonymous data numerically if both values are numbers, or
stringwise if at least one is non-numeric. Like this
sub compare {
my ($a, $b) = @_;
($a =~ /\D/ or $b =~ /\D/) ?
$a cmp $b :
$a <=> $b
}
Then you can just rewrite you sort call as below.
I hope this helps.
Rob
my @data = (
{ name => 'aaa', age => 1 },
{ name => 'bbb', age => 7 },
{ name => 'aaa', age => 11 },
{ name => 'bbb', age => 4 },
{ name => 'aaa', age => 9 },
{ name => 'bbb', age => 13 },
{ name => 'aaa', age => 8 },
{ name => 'bbb', age => 12 },
{ name => 'aaa', age => 22 },
{ name => 'bbb', age => 18 },
{ name => 'aaa', age => 24 },
{ name => 'bbb', age => 44 },
{ name => 'aaa', age => 30 },
{ name => 'bbb', age => 51 },
{ name => 'aaa', age => 66 },
{ name => 'bbb', age => 60 },
);
@data = sort {
compare($a->{name}, $b->{name}) ||
compare($a->{age}, $b->{age})
} @data;
print $_->{name}, ": ", $_->{age}, "\n"
foreach @data;
>>OUTPUT<<
aaa: 1
aaa: 8
aaa: 9
aaa: 11
aaa: 22
aaa: 24
aaa: 30
aaa: 66
bbb: 4
bbb: 7
bbb: 12
bbb: 13
bbb: 18
bbb: 44
bbb: 51
bbb: 60
Thread Previous
|
Thread Next