Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I want to sort array of arrays in Perl, but the result is not sorted

Tags:

sorting

perl

I have an array of arrays that I want to sort. Each element of array A is an array with 3 elements. Array A looks like:

my @A = ([2,3,1], [1,2,3], [1,0,2], [3,1,2], [2,2,4]);

I want to sort A in ascending order. When comparing 2 elements, the first number is used. If there is a tie, the second number is used, and then the third number.

Here is my code. I use a function 'cmpfunc' to compare 2 elements.

sub cmpfunc {
    return ($a->[0] <=> $b->[0]) or 
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]);
}
my @B = sort cmpfunc @A;
print "Result:\n";
for my $element (@B) {
    print join(",", @{$element}) . "\n";
}

Result:

1,2,3
1,0,2
2,3,1
2,2,4
3,1,2

The result is somewhat sorted, but not correct. What I expect is:

1,0,2
1,2,3
2,2,4
2,3,1
3,1,2

Is there any error in my comparison function? The strange thing is, when I put the comparison code in block, the result is correctly sorted.

my @C = sort { ($a->[0] <=> $b->[0]) or 
               ($a->[1] <=> $b->[1]) or
               ($a->[2] <=> $b->[2]) } @A;
like image 988
jftsai Avatar asked Jan 13 '12 12:01

jftsai


People also ask

How do I sort an array of elements in Perl?

Perl has a built-in sort() function to sort an array of alphabets and numbers. When an array is passed to the sort() function it returns a sorted array. Sorting of Arrays in Perl can be done in multiple ways: Use of ASCII values to sort an Array.

How do you sort an array of objects based on another array?

If you use the native array sort function, you can pass in a custom comparator to be used when sorting the array. The comparator should return a negative number if the first value is less than the second, zero if they're equal, and a positive number if the first value is greater.

How do you sort a specific part of an array?

Arrays. sort() method can be used to sort a subset of the array elements in Java. This method has three arguments i.e. the array to be sorted, the index of the first element of the subset (included in the sorted elements) and the index of the last element of the subset (excluded from the sorted elements).


2 Answers

You are executing

return ($a->[0] <=> $b->[0])

which returns before it gets to any of the "or" clauses.

Either remove the "return" keyword, or add parenthesis around the entire arg list for return:

sub cmpfunc {
    return(($a->[0] <=> $b->[0]) or
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]));
}
like image 132
tadmc Avatar answered Oct 03 '22 22:10

tadmc


The reason you observe this "wrong" behavior is the priority of or operator, the lowest possible. In this situation it means that

return ($a->[0] <=> $b->[0]) or 
       ($a->[1] <=> $b->[1]) or
       ($a->[2] <=> $b->[2]);

is interpreted as OR-ing

return ($a->[0] <=> $b->[0])

and the rest of the line -- nonsense in this case, as return never returns. :)

So you should use C's OR:

return ($a->[0] <=> $b->[0]) || 
       ($a->[1] <=> $b->[1]) ||
       ($a->[2] <=> $b->[2]);
like image 35
pwes Avatar answered Oct 03 '22 23:10

pwes