poage@sunny.ucdavis.edu (Tom Poage) (08/10/90)
I've seen a few multi-field record sorts posted here, and have taken a crack at writing one too. The base of this stems from an almost-one-liner by Randal. I tried to generalize it somewhat. The array @sortkeys contains indices of the fields the data is to be sorted on, in primary, secondary, ... order. The array @keytypes indicates what `types' each field is, implying how to compare the fields to be sorted. Valid key types for now are 'a' for string type (mnemonic ASCII) and 'n' for numeric types. I supposed this could be extended to dates and whatever else you can dream up. The sorting subroutine is assembled, based on the sort keys and key types, then eval'd to define it. The name of the sort routine in this case is `bydatakeys'. Even though the data fields in this example are separated by `;', the sort keys are passed to the bydatakeys routine joined by the `$;' value. I chose to assemble the routine and eval it instead of statically defining the routine with a key type comparison loop because I thought It'd be faster. I didn't test the difference in speed though. If you see any room for improvement (speed!, size, etc.), please respond. For me, at least, this may be useful and general enough, with enough hacking, to package it. Thanks. Tom. ================================================================= #!/bin/perl # An example of a multifield sort. @data = ( 'isajl;c;z;3;b', 'hello;a;y;1;b', 'testz;e;x;1;f', 'thisx;b;w;2;c', 'ardes;d;v;4;z', 'ardes;d;v;4;e' ); @sortkeys = ($[ + 3, $[ + 4); # Indices of sort fields. @keytypes = ('a','a','a','n','a'); # One type for each field. # Build a custom sort routine depeding on the data type(s) # indicated by $keytypes[$sortkeys[$n]]. $sortsub = 'sub bydatakeys { local(@k1) = split($;, $datakeys[$a]); local(@k2) = split($;, $datakeys[$b]);'; foreach ($[ .. $#sortkeys) { if ($keytypes[$sortkeys[$_]] eq 'n') { $gt = '>'; $lt = '<'; } elsif ($keytypes[$sortkeys[$_]] eq 'a') { $gt = 'gt'; $lt = 'lt'; } else { die "Unknown sort key type! `$keytypes[$sortkeys[$i]]'\n"; } $sortsub .= " return 1 if \$k1[$_] $gt \$k2[$_]; return -1 if \$k1[$_] $lt \$k2[$_];"; } $sortsub .= "\n\treturn 0;\n}\n"; eval $sortsub; foreach (@data) { push(@datakeys, join($;, (split(/;/))[@sortkeys])); } foreach (@data[sort bydatakeys $[ .. $#data]) { print $_, "\n"; }; # --OR-- #@sortdata = @data[sort bydatakeys $[ .. $#data]; #foreach (@sortdata) { print $_, "\n"; } -- Tom Poage, Clinical Engineering Universiy of California, Davis, Medical Center, Sacramento, CA poage@sunny.ucdavis.edu {...,ucbvax,uunet}!ucdavis!sunny!poage