Gossamer Forum
Home : General : Perl Programming :

Sorting hashes of arrays

Quote Reply
Sorting hashes of arrays
I'm trying to sort the printed output of a hash generated by a foreach loop, by the first value of each hash generated.
Here is my original code:

---------------------------------------------

my %output = ();
my @data = ($score,$full_name,$sex,$mobile,$email,$post_code,$uid);

$output{$data[0]} = [ @data ]; # make output hash of @data array
my @row_key = (keys %output);
foreach $row_key (@row_key) {
# get the rest of the data from the output hash
my ($td1, $td2, $td3, $td4, $td5, $td6, $td7) = @{$output{$row_key}};
# make a row of table data to print
my %row = (
td1 => $td1,
td2 => $td2,
td3 => $td3,
td4 => $td4,
td5 => $td5,
td6 => $td6,
td7 => $td7,
);

push(@loop, $td1);
my @sorted = sort { $a->[0] <=> $b->[0] } @loop;
foreach my $i (@sorted) {
print "$i\n";
}
-------------------------------------------

Just trying to sort by the first element of each row (td1) numerically and its not happening. @data is a multidimensional array itself populated by a foreach loop.

Any help hugely appreciated!

Phil Lamond, Manchester, UK.
Quote Reply
Re: [phillamond] Sorting hashes of arrays In reply to
I'm going to have to talk my way through this to make sure I'm understanding ;)

Code:
# Initialize the hash
my %output = ();

# Add some scalars into the array.
my @data = ($score,$full_name,$sex,$mobile,$email,$post_code,$uid);

# Ok at this point a hash key has been created with the value of $score and the value is a reference to the data array.
$output{$data[0]} = [ @data ]; # make output hash of @data array

# Now @row_key contains all the score keys.
my @row_key = (keys %output);

# Start looping the keys
foreach $row_key (@row_key) {

# Now you are deferencing the key value and assigning to multiple scalars.
my ($td1, $td2, $td3, $td4, $td5, $td6, $td7) = @{$output{$row_key}};

# Ok this is where things aren't going as you think. $td1, $td2 are now all plain scalars.
my %row = (
td1 => $td1,
td2 => $td2,
td3 => $td3,
td4 => $td4,
td5 => $td5,
td6 => $td6,
td7 => $td7,
);

# You are pushing the $score into @loop which you already have in @row_key
push(@loop, $td1);

# Now you are trying to sort a list of $scores but you are assuming they are references to arrays, but they are not.
my @sorted = sort { $a->[0] <=> $b->[0] } @loop;

# Now you are just printing out the numbers in the same order because the sort was invalid.
foreach my $i (@sorted) {
print "$i\n";
}

Does that explanation help you see where you are going wrong?
Quote Reply
Re: [Paul] Sorting hashes of arrays In reply to
Yes, definitely helps - thank you. At which stage should I be sorting then - if I do this:

----------------------------------
my %output = ();
my @data = ($score,$full_name,$sex,$mobile,$email,$post_code,$uid);
$output{$data[0]} = [ @data ]; # make output hash of @data array
my @row_key = (keys %output);
foreach $row_key (sort { $a <=> $b } @row_key) {
# get the rest of the data from the output hash
my ($td1, $td2, $td3, $td4, $td5, $td6, $td7) = @{$output{$row_key}}; # dereference to multiple scalars
print "$td1\n";
}
----------------------------------------

I get the same result - where should I apply the sort so that its valid?

Many thanks

P
Quote Reply
Re: [phillamond] Sorting hashes of arrays In reply to
>>
my @row_key = (keys %output);
foreach $row_key (sort { $a <=> $b } @row_key) {
<<

You could get rid of that first line and just do:

Code:
foreach $row_key (sort keys %output) {
Quote Reply
Re: [Paul] Sorting hashes of arrays In reply to
It must be something about the way I'm creating my @data array. The things just refuses to sort by score. Here is the basic program. If you spot anything anomalous let me know:

----------------------------------
#!/usr/bin/perl

my $dirname = "/home/phill/files";
my $file = "";
my @files = ();
chdir $dirname;
opendir (DIR, $dirname) or die print "Can't open directory $dirname: $!\n";
while (defined($file = readdir(DIR))) {
next if $file =~ /^\.\.?$/ or $file =~/jpg$/i or $file =~/gif$/i; # skip . and .. and *.jpg and *.gif
push(@files, $file) if -T $file; # text files only
my %file_data = ();
my ($full_name,$mobile,$email,$post_code);
my $uid = $file;
my ($ys_total,$yt_total,$yp_total,$training_hours,$coach_teacher); # psychometric test values
my @data = ();
my %output = ();
my @loop = ();
# open file, read all name-value pairs into hash
my $read_file = "";
open (FILE, "< $file") or die print "Couldn't open file $file for reading: $!\n";
while (<FILE>) {
$read_file .= $_;
for(split(/\n+/, $read_file)) {
($key, $val) = split(/=/);
foreach $key (keys %file_data) {
$val = $file_data{$key};
}
}
# pattern match all questionnaire keys and add up values
unless ($val eq "" || !defined $val) {
$full_name = $val if $key =~ /full_name/;
$sex = $val if $key =~ /sex/;
$mobile = $val if $key =~ /mobile_telephone/;
$email = $val if $key =~ /email/;
$post_code = $val if $key =~ /post_code/;
$training_hours = $val if $key =~ /training_hours/; # single value
$coach_teacher = $val if $key =~ /coach_teacher/; # single value
$ys_total += $val if $key =~ /your_sport\d$/; # cumulative
$yt_total += $val if $key =~ /your_training\d$/; # cumulative
$yp_total += $val if $key =~ /your_performance\d$/; # cumulative
}
$score = ($ys_total + $yt_total + $yp_total + $training_hours + $coach_teacher); # total score
$uid =~ s/\.txt//;
@data = ($score,$full_name,$sex,$mobile,$email,$post_code,$uid);
}

$output{$data[0]} = [ @data ]; # make output hash of @data array
foreach my $row_key (sort { $a <=> $b } keys %output) {
# get the rest of the data from the output hash
my ($td1, $td2, $td3, $td4, $td5, $td6, $td7) = @{$output{$row_key}}; # dereference to multiple scalars
print "$td1\t$td2\t$td3\t$td4\t$td5\t$td6\t$td7\n";
}
}
closedir(DIR);
------------------------------------

Thanks again for your time.

P