MathCurvesSurfacesWallpaper GroupsGallerySoftwarePOV-Ray
ProgramingLinuxPerl PythonHTMLCSSJavaScriptPHPJavaEmacsUnicode ♥
Web Hosting by 1&1

Perl: General Function for Sorting Matrix

Xah Lee,

Here's a function that can sort a matrix in all possible ways.

the following Python version i wrote in 2006, but was incorrect.

# -*- coding: utf-8 -*-
# python

# WARNING: THE CODE IS INCORRECT.

# sort_matrix( matrix, [[n1, s1, d1], [n2, s2, d2], [n3, s3, d3], …]) returns a sorted matrix by n1 th column, if tie, then by n2 th column … and so on.

# The first argument is a list, whose elements are lists of equal lengths.

# s1, s2, s3… are booleans. If True, then the corresponding column are interpreted as a string and the ordering is lexicographical.

# d1, d2, d3… are booleans. If True, the sort for the corresponding column are ascending.

# Example:.

# myMatrix =
#  [
#    [3, 99, 'a'],
#    [2, 77, 'a'],
#    [1, 77, 'a']
#  ];

# sort_matrix(myMatrix,[[3,True,True],[2,False,True]])

# This means sort by 3th column, regarding it as strings, and in ascending order. If tie, sort by 2th column, regarding it as number, in ascending order. It returns:

# [[2,77,'a'],
#  [1,77,'a'],
#  [3,99,'a']]

def sort_matrix(matrix, directives):
    result=matrix
    for dir in directives:
        if dir[1]: # consider as string
            if dir[2]: # ascending
                result.sort(lambda x,y: cmp( str(x[dir[0]-1]), str(y[dir[0]-1])) )
            else:      # descending
                result.sort(lambda x,y: cmp( str(x[dir[0]-1]), str(y[dir[0]-1])), None, True)
        else: # consider as number
            if dir[2]: # ascending
                result.sort(lambda x,y: cmp(float(x[dir[0]-1]), float(y[dir[0]-1])) )
            else:      # decending
                result.sort(lambda x,y: cmp(float(x[dir[0]-1]), float(y[dir[0]-1])), None, True )
    return result

Perl

This Perl version i wrote in 1999, and was put in production use in a ecommerce server.

# -*- coding: utf-8 -*-
# perl

=pod

sort_matrix( $matrix, [[$n1, $stringQ, $directionQ], [$n2, $stringQ,
$directionQ], …]) sorts a matrix by $n1 th column then $n2 th…and
so on.

$matrix must be a reference to references of arrays, having the form
[[$e1, $e2,…], […], …].  $stringQ is a boolean indicating
whether to treat corresponding columns as a strings instead of as
number in the sorting process. True means string. $directionQ is a
boolean indicating ascending sort or not for the corresponding
column. In the column spec $n1 $n2 …, index counting starts at 0.

 Example:

 my $ref_matrix =
 [
   [3, 99, 'a'],
   [2, 77, 'a'],
   [1, 77, 'a']
 ];

sort_matrix( $ref_matrix,  [ [2,1,1], [1,0,1] ]);
# this means sort by third column, regarding it as strings, 
# and in ascending order. If tie, sort by second column, 
# regarding it as number, in ascending order.

# returns [[2,77,'a'],[1,77,'a'],[3,99,'a']];

=cut


sub sort_matrix($$) {
    my $ref_matrix = $_[0];
    my @indexMatrix = @{$_[1]};

    my @indexes = map {$_->[0]} @indexMatrix;
    my @operators = map {$_->[1] ? ' cmp ' : ' <=> '} @indexMatrix;
    my @directions = map {$_->[2]} @indexMatrix;

    my $body_code = '';
    my @body_array;
    for (my $i = 0; $i <= $#indexes; $i++) {
        if ($directions[$i]) {
            push(@body_array, "(\$a->[$i]" . $operators[$i]  . "\$b->[$i])");
        } else {
            push(@body_array, "(\$b->[$i]" . $operators[$i]  . "\$a->[$i])");
        };
    };
    $body_code = join( ' or ', @body_array);

    my $array_code = '(map { [' . join(q(, ), map {"\$_->[$_]"} @indexes) . ', $_]} @$ref_matrix)';

    my $code = "map {\$_->[-1]} (sort { $body_code} $array_code)";
    my @result = eval $code;
    return [@result];
};
blog comments powered by Disqus