Took me some time, but...

On Mon, May 06, 2002 at 04:41:03PM +0100, Richard Adams wrote:
> Now I've got an array of hashes, where each hash can have  different keys
> e.g.,
> 
> @aoh = ( {H3 =>234, H1 =>127, DNA =>1, p135 =>167},
>             {H4=>24,  H3=>1550, DNA =>25, p39 =>67},
>             {H3 =>34, H2A =>125, DNA =>5, p32 =>7},
>             {H3 =>24, H4 =>156, DNA =>123, p12 =>13}
>     ) 
> And I'd like to order the array elements by virtue of the biggest value in
> the hash. 
> 
> 1. H3 1550
> 2. H3 234
> 3. H4 156
> 4. H2A 125

....I'll call it the "Partial Schwartzian Transformation on Acid" ;-)

Ok, ok, two messages ago I called Jason Friswold evil, but I just
couldn't resist.  >:->


So, this one's not for the faint of heart (tm), and for educational
purposes only:

    ---------- snip ----------
    #!/usr/bin/perl

    use strict;
    use warnings;
    use Data::Dumper;

    my @aoh = (
        {H3 =>234, H1 =>127, DNA =>1, p135 =>167},
        {H4=>24,  H3=>1550, DNA =>25, p39 =>67},
        {H3 =>34, H2A =>125, DNA =>5, p32 =>7},
        {H3 =>24, H4 =>156, DNA =>123, p12 =>13}
    );

    my @xsorted = sort {
            $b->[0]{$b->[1]} <=> $a->[0]{$a->[1]};
        } map {
            [ $_, (sort { $_->{$b} <=> $_->{$a} } keys %$_)[0] ]
        } @aoh;
    print Dumper(\@xsorted);

    print "$_->[1]: $_->[0]{$_->[1]}\n" foreach @xsorted;
    ---------- snip ----------

Let's dissect that a littlebit:

First of all, the Schwartzian Transformation is usually used as an
optimization for sorting.  You use it if the 'per-compare' computation
is rather difficult.  For an array @l it goes like this:

    A. create a temp list from @l with
        a. the actual data from @l
        b. the precomputed data from @l that's used for comparing 2
           elements during sorting.

    B. sort that temp list using the precomputed field.

    C. create the sorted list by extracting the 'pure' data from @l from
      the now sorted temp list

    D. do it all in a fat combination of map sort map >:->

We need to skip step C, since we'd lose the information about the
highest hash value if we just put all the data into the resulting array
again.

If you reverse-read above '@xsorted = ...' code you find

    A. create a temp list from @l..:

        map {
            [
              # ...with the actual data from @l...
              $_,
              # ...and the precomputed...
              (sort { $_->{$b} <=> $_->{$a} } keys %$_)[0]
            ]
        } @aoh

       For each element in @aoh we're sorting the keys of that hash by
       comparing the hash's value at that key.

       Then we use only the first element of that resulting sorted list
       of hash keys, since that has the highest value.

       So, now we have a list
            (
              [ original_item, highest_hash_key_for_that_item ],
              ...
            )
        

    B. sort that temp list using the precomputed fields...

        sort {
            $b->[0]{$b->[1]} <=> $a->[0]{$a->[1]}
        }

       We can split that up and be a bit more verbose:

        sort {
            my ($a_orig_item, $a_key) = @$a;
            my ($b_orig_item, $b_key) = @$b;

            $b_orig_item->{$b_key} <=> $a_orig_item->{$a_key}
        }

       which is a bit longer but doesn't scare the kids that much.

Now on for the questions and flames...

-- 
                       If we fail, we will lose the war.

Michael Lamertz                        |      +49 221 445420 / +49 171 6900 310
Nordstr. 49                            |                       [EMAIL PROTECTED]
50733 Cologne                          |                 http://www.lamertz.net
Germany                                |               http://www.perl-ronin.de 

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to