![]() |
|
P is for Practical | |
PerlMonks |
Generate Dynamic Sort Expressionsby lofichurch (Beadle) |
on Nov 27, 2002 at 18:48 UTC ( #216152=snippet: print w/replies, xml ) | Need Help?? |
The data retrieved from the DB is stored in a 2-d hash with each top-level key being the record #, and having keys that represent 'columns' of data below each of those. So, with that in mind -- here's the (simple) solution to doing dynamic sorts on column-based data. A sub that accepts the columns to sort by, in order, as an arrayref, and then constructs the expression for sort, then executes it for you. sub is_num_col { # do something to look up whether or not # a given column is numeric or # alphabetic. I use a hash for lookup, # e.g.: return($hash{"$_[0]"}); # # should return 1 if the column is # numeric, 0 otherwise. } sub sort_hash { # this sub creates a sort expression on-the-fly # given an array reference containing the keys # (column names) to sort a 2-d hash by. The 2-d # hash should be provided by reference as the second # argument to the sub. # # The 2-d hash model is used in that the first # dimension referrs to a record (or row), and the second # dimension to the columns within that record. # # An array reference is returned, where each element # is a key from hashref, in the order returned from the sort # # If you wish to reverse the sort order (descending) for # a column/key, simply add 'r-' to the column name. # # example: # # my $aRef = sort_hash(['id','r-name','type'],\%hash); # # Where it will first sort the hash by the id key, then # (descending) by the name key, and then by the type key. my $aRef = shift; my $hRef = shift; die("[sort_hash] Not enough Arguments!\n") if(!defined($aRef) || !def +ined($hRef)); die("[sort_hash] Incorrect Arguments!\n") if(ref($aRef) ne 'ARRAY' || + ref($hRef) ne 'HASH'); my @tests; foreach my $column (@{ $aRef }) { next if(!defined($column)); my $rev = 0; if($column =~ /^r-(.*)$/) { $rev = 1; $column = $1; } if( is_num_col($column) ) { # if this is a numeric column... if($rev == 1) { # descending push(@tests,"\$hRef->{\"\$b\"}{'$column'} <=> \$hRef->{\"\ +$a\"}{'$column'}"); } else { #ascending push(@tests,"\$hRef->{\"\$a\"}{'$column'} <=> \$hRef-> +{\"\$b\"}{'$column'}"); } } else { # if this is an alphabetic column... if($rev == 1) { # descending push(@tests,"\$hRef->{\"\$b\"}{'$column'} cmp \$hRef-> +{\"\$a\"}{'$column'}"); } else { #ascending push(@tests,"\$hRef->{\"\$a\"}{'$column'} cmp \$hR +ef->{\"\$b\"}{'$column'}"); } } } #end foreach # now, create our actual expression via join... my $sort_expr = join(' || ',@tests); # we create an anonymous subref, as doing an eval inside of # the sort's BLOCK can occaisonally cause problems, especially # nested way down in some module. my $SortSub = sub { return(eval $sort_expr); }; my @sorted = sort { &$SortSub } keys(%{ $hRef }); return(\@sorted); }
|
|