arbitrary sorting...

Tkil tkil at scrye.com
Mon Jul 15 01:26:59 CDT 2002


>>>>> "Kari" == Kari Chisholm <karic at lclark.edu> writes:

Kari> I want to sort it, but I don't want to sort it alphabetically or
Kari> in any other predictable way.  Instead, I've got an arbitrary
Kari> best order.  Using my example, let's say that I've got 50 items
Kari> in my preferred order (@bar) such that the fruit are grouped
Kari> first, then cars, then flintstones.

Kari> What I want is to compare @foo to @bar such that I'm able to reorder @foo
Kari> into: apple banana porsche ferrari wilma barney.

if you know that @foo is a subset of @bar (that is, there are no
elements of @foo that are not also in @bar), then you can do:

| my %bar_score = do 
| {
|     my $i = 0;
|     map { $_ => ++i } reverse @bar;
| };
| my @foo_by_bar = sort { $bar_score{$a} <=> $bar_score{$b} } @foo

if you think that foo might have elements not in @bar, you can do
something like this:

| my @foo_in_bar = sort { $bar_score{$a} <=> $bar_score{$b} }
|                    grep exists($bar_score{$_}) @bar;
| my @foo_not_in_bar = grep !exists($bar_score{$_}), @foo;
| my @foo_by_bar_with_stragglers = ( @foo_by_bar, @foo_not_in_bar );

the basic idea is to preprocess the "preferred order" (@bar) to
establish a "score" for each one (%bar_score), then rank the raw data
according to that score.  

the "reverse" in the bar_score creation is there so that the first
element of @bar has the highest value; you could simplify that in
various ways, including some that are dynamic (as you see new
elements, you can add them to the end of the list with lower ranking;
this resembles a "first come, first served" arrangement where elements
might appear again, and should be filed with others of their ilk.

if you really want to sort by type, you can extend this idea with
another level of indirection (which describes a very great many
computer science solutions!):

| my @info = ( { type       => 'car',
|                preference => 1000,
|                elements   => [ qw( porsche ferrari
|                                    bmw audi subaru ) ] },
|              { type       => 'fruit',
|                preference => 500,
|                elements   => [ qw( bananna pear apple
|                                    mango merlyn fig ) ] },
|                 
|              { type       => 'toons',
|                preference => 100,
|                elements   => [ qw( fred wilma betty barnie
|                                    stan kyle cartman kenny ) ] } );
| 
| my %pref_of_thing;
| foreach $i (@info)
| {
|     my $el = $i->{elements};
|     @pref_of_thing{ @$el } = ( $i->{preference} ) x @$el;
| }
| 
| my @bar_by_preference =
|   sort { $pref_of_thing{$a} <=> $pref_of_thing{$b} } @bar;

note that @info could easily come from a database.  in this particular
case, the "type" information isn't really used, but you can see how
you might prefer to use that instead...

i also don't know how stable perl's sort is; if a bunch of things
compare equal, i don't know if they will be reordered or not in the
output.

t.
TIMTOWTDI



More information about the Pdx-pm-list mailing list