[comp.lang.perl] YAMFRS

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