use hash of arrays to sort

Y

yeti349

Hi, I'm attempting to use a hash of arrays to sort columns of data with
the option of sorting by ascending and descending order. Does this seem
like a valid approach? How could I add "$b[0] <=> $a[0]" to do
descending sorts as well. Thanks.

Here is my code so far.

#!/usr/bin/perl

use strict;
use warnings;

my %results_h;
my @results_b;

while (<DATA>) {
next unless s/^(.*?):\s*//;
push @results_b, [split];
}

foreach (@results_b) {
$results_h{$_->[0]} = $_; #sort by this column
}

foreach (sort keys(%results_h)) {
print join(" ",@{$results_h{$_}}),"\n";
}

__DATA__
1:frodo 00002
2:bob 00001
3:eddie 00004
4:fred 00003
 
X

xhoster

Hi, I'm attempting to use a hash of arrays to sort columns of data with
the option of sorting by ascending and descending order. Does this seem
like a valid approach?

I don't see the point in using the hash at all. It adds overhead, and
causes problems if the sort field is not unique. Why not just sort like
this:?

my $field=0; #field to sort by

my @new_order = sort {$a->[$field] cmp $b->[$field]} @results_b;

(or you could stuff the results back into @results_b, if you don't need
to save a copy of the original order)

How could I add "$b[0] <=> $a[0]" to do
descending sorts as well. Thanks.

The sort you have now is equivalent to "{$a cmp $b}". So you would add
"{$b cmp $a}" to reverse it. (or just "reverse" the list resulting from
the sort). If you change to the sort method I suggest, than I trust the
way to reverse it is obvious.

Note that if you want <=>, then cmp is the wrong thing. And if you want
cmp, then <=> is the wrong thing.

Xho
 
P

Paul Lalli

Hi, I'm attempting to use a hash of arrays to sort columns of data with
the option of sorting by ascending and descending order. Does this seem
like a valid approach?

Why do you want to do this? What is wrong with the myriad of other
sorting techniques already written? (For example, Sort::Fields -
http://search.cpan.org/~jnh/Sort-Fields-0.90/Fields.pm ) Re-inventing
the wheel is rarely "a valid approach".
How could I add "$b[0] <=> $a[0]" to do
descending sorts as well. Thanks.

I don't understand how that line makes sense with the data you have, or
how you're asking it be integrated. Perhaps you could clarify?

Paul Lalli
 
Y

yeti349

thanks for the responses. Here is what I've come up with. I'm getting
the following error however: "Can't use string ("13961170005719 FINC
D 0965874") as an ARRAY ref while "strict refs" in use at sort.pl line
37, <DATA> line 10."

This is probably a simple error, but I'm stumped. Thanks.

#!/usr/bin/perl

use strict;
use warnings;
use CGI qw:)standard);

my @results_b;
my $field = '';
my @sorted;

my $sort_by = param("sort_by");

while (<DATA>) {
@results_b = split('\t');
}

#sort any column by ascending(0) or descending(1)
if ($sort_by eq 'a_sa') { @sorted = sort_fields(0,0,@results_b); }
elsif ($sort_by eq 'd_sa') { @sorted = sort_fields(0,1,@results_b); }

elsif ($sort_by eq 'a_name') { @sorted = sort_fields(1,0,@results_b); }
elsif ($sort_by eq 'd_name') { @sorted = sort_fields(1,1,@results_b); }

elsif ($sort_by eq 'a_vt') { @sorted = sort_fields(2,0,@results_b); }
elsif ($sort_by eq 'd_vt') { @sorted = sort_fields(2,1,@results_b); }

elsif ($sort_by eq 'a_sss') { @sorted = sort_fields(3,0,@results_b); }
elsif ($sort_by eq 'd_sss') { @sorted = sort_fields(3,1,@results_b); }

elsif ($sort_by eq 'a_dob') { @sorted = sort_fields(4,0,@results_b); }
elsif ($sort_by eq 'd_dob') { @sorted = sort_fields(4,1,@results_b); }

elsif ($sort_by eq 'a_date') { @sorted = sort_fields(5,0,@results_b); }
elsif ($sort_by eq 'd_date') { @sorted = sort_fields(5,1,@results_b); }


foreach(@sorted){print"@$_\n";}

sub sort_fields {
my ($field,$sort_by,@results_b) = @_;
if ($sort_by == 0) { my @new_order = sort {$a->[$field] cmp
$b->[$field]} @results_b; }
else { my @new_order = sort {$b->[$field] cmp $a->[$field]}
@results_b; }
}

__DATA__
11960990009910 FENOFF A 001587407 03021976 08012005
18960990014211 MARKEM C 030350756 04141929 08032005
89961060004312 ENOS B 034401255 02071951 08012003
19961060012013 WOLF E 051667116 unknown 08011999
79961080011214 MAPLEDALE I 161029165 04131952 02012005
17961140002215 LAU J unknown 05091950 08112005
12961140017216 FINNERAN F 000000000 02121964 02012001
49961150000117 GALLOWAY H 007680783 02181960 11012005
16961150004018 BANK G 000000000 05111960 04122003
13961170005719 FINC D 096587493 10041970 04012005
 
P

Paul Lalli

thanks for the responses.

To whom are you speaking? For what responses are you thanking people?
Please quote some context when replying.

Have you read the Posting Guidelines for this group?
Here is what I've come up with.

You have yet to tell us why you feel the need to reinvent a wheel
that's been invented at least three times (as a search for 'sort' on
CPAN reveals)
I'm getting
the following error however: "Can't use string ("13961170005719 FINC
D 0965874") as an ARRAY ref while "strict refs" in use at sort.pl line
37, <DATA> line 10."

This is probably a simple error, but I'm stumped. Thanks.

#!/usr/bin/perl

use strict;
use warnings;
use CGI qw:)standard);

my @results_b;
my $field = '';
my @sorted;

my $sort_by = param("sort_by");

while (<DATA>) {
@results_b = split('\t');
}

Each time through the loop, you're assigning @results_b to be the
return value of split('\t'). Whatever was in @results_b after the last
iteration is wiped out completely. I'm guessing you meant to assign
the results of split to an array reference, and then add that array
reference to @results_b

while (<DATA>) {
my $newref = [ split /\t/ ];
push @results_b, $newref;
#or, in one step instead of two:
# push @results_b, [ split /\t/ ];
}

In the future, a good debugging technique would be to analyze your data
structures to make sure they contain what you think they contain:

use Data::Dumper;
print Dumper(\@results_b);

would have shown you that @results_b contained only three strings, not
a series of references to arrays containing three strings.

Paul Lalli
 
J

John W. Krahn

thanks for the responses. Here is what I've come up with. I'm getting
the following error however: "Can't use string ("13961170005719 FINC
D 0965874") as an ARRAY ref while "strict refs" in use at sort.pl line
37, <DATA> line 10."

This is probably a simple error, but I'm stumped. Thanks.

#!/usr/bin/perl

use strict;
use warnings;
use CGI qw:)standard);

my @results_b;
my $field = '';
my @sorted;

my $sort_by = param("sort_by");

while (<DATA>) {
@results_b = split('\t');

You are getting the error because the @results_b array does not contain array
references. That should be something like:

chomp;
push @results_b, [ split /\t/ ];

}

#sort any column by ascending(0) or descending(1)
if ($sort_by eq 'a_sa') { @sorted = sort_fields(0,0,@results_b); }
elsif ($sort_by eq 'd_sa') { @sorted = sort_fields(0,1,@results_b); }

elsif ($sort_by eq 'a_name') { @sorted = sort_fields(1,0,@results_b); }
elsif ($sort_by eq 'd_name') { @sorted = sort_fields(1,1,@results_b); }

elsif ($sort_by eq 'a_vt') { @sorted = sort_fields(2,0,@results_b); }
elsif ($sort_by eq 'd_vt') { @sorted = sort_fields(2,1,@results_b); }

elsif ($sort_by eq 'a_sss') { @sorted = sort_fields(3,0,@results_b); }
elsif ($sort_by eq 'd_sss') { @sorted = sort_fields(3,1,@results_b); }

elsif ($sort_by eq 'a_dob') { @sorted = sort_fields(4,0,@results_b); }
elsif ($sort_by eq 'd_dob') { @sorted = sort_fields(4,1,@results_b); }

elsif ($sort_by eq 'a_date') { @sorted = sort_fields(5,0,@results_b); }
elsif ($sort_by eq 'd_date') { @sorted = sort_fields(5,1,@results_b); }


foreach(@sorted){print"@$_\n";}

sub sort_fields {
my ($field,$sort_by,@results_b) = @_;
if ($sort_by == 0) { my @new_order = sort {$a->[$field] cmp
$b->[$field]} @results_b; }
else { my @new_order = sort {$b->[$field] cmp $a->[$field]}
@results_b; }
}

__DATA__
11960990009910 FENOFF A 001587407 03021976 08012005
18960990014211 MARKEM C 030350756 04141929 08032005
89961060004312 ENOS B 034401255 02071951 08012003
19961060012013 WOLF E 051667116 unknown 08011999
79961080011214 MAPLEDALE I 161029165 04131952 02012005
17961140002215 LAU J unknown 05091950 08112005
12961140017216 FINNERAN F 000000000 02121964 02012001
49961150000117 GALLOWAY H 007680783 02181960 11012005
16961150004018 BANK G 000000000 05111960 04122003
13961170005719 FINC D 096587493 10041970 04012005

A more "perlish" way to do that:

#!/usr/bin/perl
use strict;
use warnings;
use CGI qw:)standard);

my %sort_fields = (
a_sa => { field => 0, ascend => 1 },
d_sa => { field => 0, ascend => 0 },
a_name => { field => 1, ascend => 1 },
d_name => { field => 1, ascend => 0 },
a_vt => { field => 2, ascend => 1 },
d_vt => { field => 2, ascend => 0 },
a_sss => { field => 3, ascend => 1 },
d_sss => { field => 3, ascend => 0 },
a_dob => { field => 4, ascend => 1 },
d_dob => { field => 4, ascend => 0 },
a_date => { field => 5, ascend => 1 },
d_date => { field => 5, ascend => 0 },
);

my $sort_by = param( 'sort_by' );

die "$sort_by is not a valid parameter.\n"
unless exists $sort_fields{ $sort_by };

my @results_b;
while ( <DATA> ) {
chomp;
push @results_b, [ split /\t/ ];
}

my $sort_sub = $sort_fields{ $sort_by }{ ascend }
? sub { $a->[ $sort_fields{ $sort_by }{ field } ] cmp $b->[ $sort_fields{
$sort_by }{ field } ] }
: sub { $b->[ $sort_fields{ $sort_by }{ field } ] cmp $a->[ $sort_fields{
$sort_by }{ field } ] };

my @sorted = sort $sort_sub @results_b;

print "@$_\n" for @sorted;

__END__




John
 
W

William James

thanks for the responses. Here is what I've come up with. I'm getting
the following error however: "Can't use string ("13961170005719 FINC
D 0965874") as an ARRAY ref while "strict refs" in use at sort.pl line
37, <DATA> line 10."

You would probably find it easier with Ruby. Note that sorting on
the dates won't do you any good until you change their format to
yyyymmdd.
-----------------------------------------------------------------
Sort_fields = %w(sa name vt sss dob date)

class Array
def mysort( field, direction=:up )
raise "#{field} is invalid argument" unless
Sort_fields.include?(field)
out = sort_by{ |x| x[ Sort_fields.index( field ) ] }
out.reverse! if :down == direction
out
end
end

results = DATA.to_a.map{|line| line.split }
puts results.mysort( 'name' ).map{|x| x.join(" ")}
puts "-----"
puts results.mysort( 'sa', :down ).map{|x| x.join(" ")}
puts "-----"
puts results.mysort( 'sss', :up ).map{|x| x.join(" ")}

__END__
11960990009910 FENOFF A 001587407 03021976 08012005
18960990014211 MARKEM C 030350756 04141929 08032005
89961060004312 ENOS B 034401255 02071951 08012003
19961060012013 WOLF E 051667116 unknown 08011999
79961080011214 MAPLEDALE I 161029165 04131952 02012005
17961140002215 LAU J unknown 05091950 08112005
12961140017216 FINNERAN F 000000000 02121964 02012001
49961150000117 GALLOWAY H 007680783 02181960 11012005
16961150004018 BANK G 000000000 05111960 04122003
13961170005719 FINC D 096587493 10041970 04012005
----------------------------------------------------------------

Output:

16961150004018 BANK G 000000000 05111960 04122003
89961060004312 ENOS B 034401255 02071951 08012003
11960990009910 FENOFF A 001587407 03021976 08012005
13961170005719 FINC D 096587493 10041970 04012005
12961140017216 FINNERAN F 000000000 02121964 02012001
49961150000117 GALLOWAY H 007680783 02181960 11012005
17961140002215 LAU J unknown 05091950 08112005
79961080011214 MAPLEDALE I 161029165 04131952 02012005
18960990014211 MARKEM C 030350756 04141929 08032005
19961060012013 WOLF E 051667116 unknown 08011999
-----
89961060004312 ENOS B 034401255 02071951 08012003
79961080011214 MAPLEDALE I 161029165 04131952 02012005
49961150000117 GALLOWAY H 007680783 02181960 11012005
19961060012013 WOLF E 051667116 unknown 08011999
18960990014211 MARKEM C 030350756 04141929 08032005
17961140002215 LAU J unknown 05091950 08112005
16961150004018 BANK G 000000000 05111960 04122003
13961170005719 FINC D 096587493 10041970 04012005
12961140017216 FINNERAN F 000000000 02121964 02012001
11960990009910 FENOFF A 001587407 03021976 08012005
-----
12961140017216 FINNERAN F 000000000 02121964 02012001
16961150004018 BANK G 000000000 05111960 04122003
11960990009910 FENOFF A 001587407 03021976 08012005
49961150000117 GALLOWAY H 007680783 02181960 11012005
18960990014211 MARKEM C 030350756 04141929 08032005
89961060004312 ENOS B 034401255 02071951 08012003
19961060012013 WOLF E 051667116 unknown 08011999
13961170005719 FINC D 096587493 10041970 04012005
79961080011214 MAPLEDALE I 161029165 04131952 02012005
17961140002215 LAU J unknown 05091950 08112005
 

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

Similar Threads


Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top