CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
Sort Multidimensional Hashes in Perl

 



yasdel
Novice

Sep 6, 2010, 9:40 AM

Post #1 of 4 (1690 views)
Sort Multidimensional Hashes in Perl Can't Post

Hello Everybody,

I just joined this fourum today and I am happy to be among the experts and friends with different levels of programming.

I am rather new to perl programming so appology in advance if the question looks simplistic. I would apprecitae if you could propose a clear and understandable solution for my problem. Thanks in advance ...

Here is the problem :

Suppose we have multi-dim array :

# define a hash
my %hash = (

{
KEY1 => [Alice,50,40],
KEY2 => [John,62,80],
KEY3 => [Reza,40,90],
},

);

And we want to sort this hash to give us the results :

1. sort based on value1 (which is alphabetically)

Output :
Alice,50,40
John,62,80
Reza,40,90

2. sort based on value2 (which is numerically)

Output :
Reza,40,90
Alice,50,40
John,62,80

3. sort based on value3

Output :
Alice,50,40
John,62,80
Reza,40,90


Please provide me with your ideas.

Looking forward to hearing from you.

Yas


BillKSmith
Veteran

Sep 6, 2010, 4:24 PM

Post #2 of 4 (1662 views)
Re: [yasdel] Sort Multidimensional Hashes in Perl [In reply to] Can't Post

Strictly speaking, perl does not have multi-dimensional arrays. They can be simulated very well with references. The syntax of your hash is not quite correct. As shown, you hash has only one key and the corresponding value is undefined. (The key is a reference to an anonymous hash with three keys. The corresponding values are array references) I assume that you intend to define a hash of arrays. We must also clarify what you mean by 'sort this hash'. You have no control at all over how perl stores a hash. You can, of course, print its contents in any order you want. The sort blocks in the following code should be clear. This approach does not scale well for large hashes because of the number of times each element must be evaluated. Ask again if computational efficiency is an issue.


Code
use strict; 
use warnings;
my %hash = (

KEY1 => [ 'Alice', 50, 40 ],
KEY2 => [ 'John', 2, 80 ],
KEY3 => [ 'Reza', 40, 90 ],

);

foreach my $key (sort {$hash{$a}[0] cmp $hash{$b}[0]} keys %hash) {
print join ",", @{$hash{$key}}, "\n";
}

print "\n\n\n";
foreach my $key (sort {$hash{$a}[1] <=> $hash{$b}[1]} keys %hash) {
print join ",", @{$hash{$key}}, "\n";
}

print "\n\n\n";
foreach my $key (sort {$hash{$a}[2] <=> $hash{$b}[2]} keys %hash) {
print join ",", @{$hash{$key}}, "\n";
}

Good Luck,
Bill


yasdel
Novice

Sep 7, 2010, 6:20 AM

Post #3 of 4 (1642 views)
Re: [BillKSmith] Sort Multidimensional Hashes in Perl [In reply to] Can't Post

Dear Bill,

Thanks alot man. That was a pretty solution, just exactly what I wanted.

I will send a new post under the title " Unlimeted Hash size" an explain there my reason for choise of hash of arrays.

Best Regards,
Yas


1arryb
User

Sep 8, 2010, 8:57 AM

Post #4 of 4 (1625 views)
Re: [yasdel] Sort Multidimensional Hashes in Perl [In reply to] Can't Post

Hi yasdel,

You can also use map to flatten the hash into an array for the sort:

Code
#!/usr/bin/perl   

use strict;
use warnings;
use Getopt::Std;

# Set program defaults.
my $cmp = 'cmp'; # Sort alphabetically.
my $sortPos = 1; # The first array column.
my $aSize = 3; # Number of columns in the inner array.

my $usage =
"usage: $0 [-hn] [<sort_field_pos>]
where:
-h = Print usage and exit.
-n = Sort numerically (Default: Sort alphabetically).
<sort_field_pos> = The Inner array column to sort on (Default: The first column, \"1\").
";

# Process option flags.
my %options = ();
getopts("hn", \%options);

if ( $options{h} ) {
print "$usage";
exit 0;
}
$cmp = '<=>' if $options{n};

# Process positional parameters.
# Note: We count from pos 1 instead of 0 b/c we prepend
# the hash key to the array before sorting.
$sortPos = $ARGV[0] if $ARGV[0];
if ( $sortPos < 1 or $sortPos > $aSize ) {
die "<sort_field_pos> is out of range";
}

# define a hash. I use a hash reference b/c it makes
# the dereferencing syntax easier.
my $hash = {
KEY1 => ["Alice",50,40],
KEY2 => ["John",62,80],
KEY3 => ["Reza",40,90]
};

# Flatten the hash into an array and sort it by the given field.
# Note: The eval is just a cute way to switch between 'cmp' and '<=>'
# depending on whether we are sorting numerically. You could use
# if-then-else instead at the cost of a little code duplication.
my @values = sort { eval "\$a->[$sortPos] $cmp \$b->[$sortPos]" } map { [ $_, @{$hash->{$_}} ] } keys(%$hash);
map { print join(', ', @$_ ), "\n" } @values;


Cheers,

Larry


(This post was edited by 1arryb on Sep 8, 2010, 9:12 AM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives