Sorting an array and hash elements in Perl

Published:
Sorting in PerlThere are many situations when we need to display the data in sorted order. For example: Student details by name or by rank or by total marks etc. If you are working on data driven based projects then you will use sorting techniques very frequently.
In Perl we have sort function which sorts a list alphabetically by default. But there is not the end. We need to sort:

   
an array numerically  or
     
case insensitive strings or
     
case sensitive strings
     
hash contents by keys or
   
hash contents by values or
   
reverse of all above said points

How sorting  works in Perl
Sort subroutine has three syntax and last one is the most used syntax.

    sort SUBNAME LIST
    sort BLOCK LIST
    sort LIST

In list context, it sorts the LIST and returns the sorted list value. In scalar context, the behavior of sort() is undefined.
If SUBNAME or BLOCK is omitted, sorts in standard string comparison order.

Standard string comparison means based on ASCII value of those characters. Like @arr = qw (Call all). In this case it will be sorted as Call all which was not expected. So to make it work properly we use case-insensitive sort.

 If SUBNAME is specified, it gives the name of a subroutine that returns an integer less than, equal to, or greater than 0 , depending on how the elements of the list are to be ordered. (The <=> and cmp operators are extremely useful in such routines.)

Note: The values to be compared are always passed by reference and should not be modified . $a and $b are global variable and should not be declared as lexical variables.

sort() returns aliases into the original list like grep, map etc  which should be  usually avoided for better readability.
As sorting always does string sorting, so to do numeric sorting we need to use a special syntax which a sort {$a รณ $b} LIST. We will see these conditions using Perl codes.
 
How reverse sorting works
Syntax to use reverse sort is reverse LIST. It works on sorted LIST usually. But in scalar context, it concatenates the elements of LIST and returns a string value with all characters in the opposite order.
In scalar context if argument is not passed it will reverse the value of $_

Ex:  

$_ = "dlrow ,olleH";</span></p>
print scalar reverse;  #in this case print reverse would not works because it expects a LIST  

 
How <=> and cmp work?
These are actually binary equality operator. Binary operator usually gives (0 or 1) or (true or false)  but these gives three values based on the comparison result.
Binary  "<=>" returns -1, 0, or 1 depending on whether the left argument is numerically less than, equal to, or greater than the right argument.
Binary "cmp" returns -1, 0, or 1 depending on whether the left argument is stringwise less than, equal to, or greater than the right argument.
Never mix string and numeric values in LIST else sorting result will be fearsome :-(

Try this out:

 
      
my @arr1 = qw(1 two 3 0 4  Two 5 six 7 8 9 ten); 
                      	my @arr2 = sort {$a cmp $b} @arr1; 
                      	print "\n@arr2\n"; 
                       

Open in new window



Let go through the codes for different scenarios:
Example 1: Sorting  an array of strings (case-sensitive and case-insensitive examples)

 
                      	#!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @strarr = qw(two Two six Six alien Coders Alien coderS); 
                      	    my @sorted = sort {$a cmp $b} @strarr; # same result as of sort @strarr 
                      	    my @sortedcase  = sort { uc $a cmp uc $b } @strarr; #case-insensitivie 
                      	    print "\n@sorted\n@sortedcase\n"; 

Open in new window




Output:
 
Alien Coders Six Two alien coderS six two
alien Alien Coders coderS six Six two Two
 
Note: try to always use in-case sensitive for better string sorting results.
 
Example 2: Sorting an array of numbers
The Perl sort function sorts by strings instead of by numbers. If you do it in general way it would fetch unexpected result.
 

 #!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @numbers = (23, 1, 22, 7, 109, 9, 65, 3, 01, 001); 
                       
                      	    my @sorted_numbers = sort @numbers; 
                      	    print "@sorted_numbers\n"; 
                       

Open in new window



The output you would see would be:
    001 01 1 109 22 23 3 65 7 9

To sort numerically, declare your own sort block and use the binary equality operator i.e. flying saucer operator <=>:
 

 
        
 #!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @numbers = (23, 1, 22, 7, 109, 9, 65, 3, 01, 001); 
                       
                      	    my @sorted_numbers = sort {$a <=> $b} @numbers; 
                      	    print "@sorted_numbers\n"; 

Open in new window




The output would now be:
    1 01 001  3 7 9 22 23 65 109
Note that $a and $b do not need to be declared, even with use strict on, because they are special sorting variables.

Example 3: Sorting array backwards (for string and numbers)
To sort backwards you need to declare your own sort block, and simply put $b before $a. or use reverse keyword after simple sort.
For example, the standard sort is as follows:
 

   #!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @strings = qw(Jassi Alien Coders); 
                       
                      	    my @sorted_strings = sort @strings; 
                      	    print "@sorted_strings\n"; 
                       

Open in new window

The output would be:
    Alien Coders Jassi

To do the same, but in reverse order:

 
      
 #!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @strings = qw(Jassi Alien Coders); 
                       
                      	    my @sorted_strings = sort {$b cmp $a} @strings; # or reverse sort @strings 
                      	    print "@sorted_strings\n"; 

Open in new window




The output is:
Jassi Coders Alien

And for numbers:

 
      
 #!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @numbers = (23, 1, 22, 7, 109, 9, 65, 3); 
                       
                      	    my @sorted_numbers = sort {$b <=> $a} @numbers; # or reverse sort {$a&oacute; $b} @numbers 
                      	    print "@sorted_numbers\n"; 
                       

Open in new window



The output is:
    109 65 23 22 9 7 3 1
This was all about sorting array elements alphabetically or numerically. Now we will see how sorting works on hash elements.

Example 4: Sorting hashes by keys
You can use sort to order hashes. For example, if you had a hash as follows:
Suppose we want to display the members for each community sorted alphabetically or say by keys, then this code will do so:

 
        
#!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my %members = ( 
                      	        C => 1, 
                      	        Java => 7, 
                      	        Perl => 12, 
                      	        Linux => 3, 
                      	        Hacking => 8, 
                      	    ); 
                      	foreach my $language (sort keys %members) { 
                      	        print $language . ": " . $members{$language} . "\n"; 
                      	    } 
                       

Open in new window



Output:
    C: 1
    Hacking: 8
    Java: 7
    Linux: 3
    Perl: 12

If you want to sort the same hash by the values (i.e. the users beside each programming language), you could do the following:

 
        
 #!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my %members = ( 
                      	        C => 1, 
                      	        Java => 7, 
                      	        Perl => 12, 
                      	        Linux => 3, 
                      	        Hacking => 8, 
                      	    ); 
                      	    # Using <=> instead of cmp because of the numbers 
                      	    foreach my $language (sort {$members{$a} <=> $members{$b}} keys %members){ 
                      	                print $language . ": " . $members{$language} . "\n"; 
                      	} 
                       

Open in new window




Output:
    C: 1
    Linux: 3
    Java: 7
    Hacking: 8
    Perl: 12

Example: 5 Sorting complex data structures
We can also use sort function to sort complex data structures. For example, suppose we have an array of hashes (anonymous hashes) like:

 
      
 my @aliens = ( 
                      	        { name => 'Jassi', age => 28}, 
                      	        { name => 'Somnath', age => 27}, 
                      	        { name => 'Ritesh', age => 24}, 
                      	        { name => 'Santosh', age => 29}, 
                      	        { name => 'Ranjan', age => 26}, 
                      	        { name => 'Kaushik', age => 25}, 
                      	    ); 

Open in new window




And we wish to display the data about the people by name, in alphabetical order, we could do the following:

 
        
#!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @aliens = ( 
                      	        { name => 'Jassi', age => 28}, 
                      	        { name => 'Somnath', age => 27}, 
                      	        { name => 'Ritesh', age => 24}, 
                      	        { name => 'Santosh', age => 29}, 
                      	        { name => 'Ranjan', age => 26}, 
                      	        { name => 'Kaushik', age => 25}, 
                      	    ); 
                       
                      	    foreach my $person (sort {$a->{name} cmp $b->{name}} @aliens) { 
                      	        print $person->{name} . " is " . $person->{age} . "\n"; 
                      	    } 
                       

Open in new window

The output is:
    Jassi is 28
    Kaushik is 25
    Ranjan is 26
    Ritesh is 24
    Santosh is 29
    Somnath is 27

Sorting the same hash by age and using a subroutine (inline function)
Rather than writing the code inline, you can also pass in a subroutine name. The subroutine needs to return an integer less than, equal to, or greater than 0. Do not modify the $a and $b variables as they are passed in by reference, and modifying them will probably confuse your sorting.

 
      
#!/usr/bin/perl 
                      	    use strict; 
                      	    use warnings; 
                       
                      	    my @aliens = ( 
                      	        { name => 'Jassi', age => 28}, 
                      	        { name => 'Somnath', age => 27}, 
                      	        { name => 'Ritesh', age => 24}, 
                      	        { name => 'Santosh', age => 29}, 
                      	        { name => 'Ranjan', age => 26}, 
                      	        { name => 'Kaushik', age => 25}, 
                      	    ); 
                       
                      	    foreach my $person (sort agecomp @aliens) { 
                      	# it just replaced {$a->{age} <=> $b->{age}} by agecomp inline function 
                      	        print $person->{name} . " is " . $person->{age} . " years old\n"; 
                      	    } 
                       
                      	    sub agecomp { 
                      	        $a->{age} <=> $b->{age}; 
                      	    } 
                       

Open in new window


The output would be:
    Ritesh is 24 years old
    Kaushik is 25 years old
    Ranjan is 26 years old
    Somnath is 27 years old
    Jassi is 28 years old
    Santosh is 29 years old

To find out more on sort function,  run the command on Linux box:
    perldoc -f sort

I have published this topic at Alien Coders
and Here is the link for original article:Sorting an array and hash elements in Perl

Note: I am the owner of this website  and author of the tutorial
1
5,027 Views

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.