Clownburner has asked for the wisdom of the Perl Monks concerning the following question:

O Great Ones, I humbly submit to your wisdom:

I have a program that skims data from a database and sorts it highest values first. The data is pushed into a hash as four arrays, so in effect I have four different data points in each hash, and the hashes are stored in an array. Here's how it's populated (this is in a foreach loop):

push(@alldata, { name => $filename, data => $data });
If I sort this beastie, it works fine like this:
my @maxref = sort { $b->{data}->[$opt{data}] <=> $a->{data}->[$opt{dat +a}] } @alldata;
But I can't figure out how to sort two different arrays together, for example if I want the highest values from a combination of $opt{data} = 1 and $opt{data} = 2. I tried this:
my @maxtmp1 = sort { $b->{data}->[0] <=> $a->{data}->[0] } @alldata; my @maxtmp2 = sort { $b->{data}->[1] <=> $a->{data}->[1] } @alldata; @maxtemp = (@maxtmp1, @maxtmp2); @maxref = sort { $b->{data} <=> $a->{data} @maxtemp;
But that didn't work, and I'm wasting two sort functions, so obviously this is highly suboptimal code. I'm new to perl and although I've had some successes these multidimensional arrays are blowing my tiny mind. Any suggestions??

Any and all tidbits of wisdom or direction to a perl newbie is most welcome.


Signature void where prohibited by law.

Replies are listed 'Best First'.
Re: Help with multidimensional sorting on arrays of hashed arrays
by arturo (Vicar) on Mar 10, 2001 at 01:03 UTC

    I'm not clear I understand what you want. You say you want to "sort two different arrays together", when it seems (from your code) what you want is to sort the array by two different criteria.

    What you get now in @maxtemp are two sorted copies of your @alldata array, first by the first element of the anonymous array referenced by the 'data' key of the anonymous hash, and second by the second element of that anonymous array (oy!).

    Do you want to get a list that's ordered by the first element and have 'ties' be sorted by the second element? That looks like

    my @maxtemp = sort { $b->{data}[0] <=> $a->{data}[0] or $b->{data}[1] <=> $a->{data}[1] } @alldata;
    (if the first comparison produces 0, the second comparison will be done)

    Deep-nested data structures are pretty complex stuff to be tackling for a self-described 'newbie' =) good on ya!

    HTH

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      Close.. What I actually want to do is sort the 1st and 2nd values together, and then retrieve them in descending order. Effectively "flattening" two of the array values together (for this project, the third and fourth values are irrelevant). I'm thinking maybe something like this (does this make sense? I'm not sure what the first line will give me exactly...):
      @newarray = (@alldata{data}[0], @alldata{data}[1]) @maxref = sort { $b->{data} <=> $a->{data} } @newarray
      am I getting closer?

      PS: I think my newbie status combined with this 3-dimensional array/hash/array structure has caused my cerebral cortex to rupture and leak my sanity out into the aether. :-P

      Signature void where prohibited by law.

        OK, for those onlookers: After extensive conversation in the chatterbox, Clownburner told me the following:

        {name} and {data} arrays map 1:1 onto each other, and the first two elements of the {data} array correspond to input and output rates, while the first two elements of the {name} array correspond to filenames.

        Clownburner wants a list of filenames sorted by greatest rates (input or output).

        First, get the list of name-value pairs for the first two values of {data} from @alldata

        my @names_and_rates = map { [ $_->{name}[0], $_->{data}[0] ], [ $_->{name}[1], $_->{data}[1] ] } @alldata;

        Now what you've got is an array, each element of which is an anonymous array consisting of a name -> rate pair. i.e.:

        @names_and_rates = ( [ name1, rate1], [name2, rate2], ... );

        What you want to do is sort that array by the second element of each anonymous array, i.e.

        my @final_list = sort { $b->[1] <=> $a->[1] } @names_and_rates; # $b first for descending order

        I think that will get you what you want. You can, of course, combine the two statements into one, e.g.

        my @final_list = sort { #sort stuff } map { #map stuff } @alldata;

        but I think that might be confusing if you come back to this code later, especially given the farrago of syntax. YMMV, IANAProfessional, etc.

        I hope this is right and that it helps.

        a nicety : if you want to print out the top n, do this:

        print "File\t\tRate\n"; foreach (@final_list[0 .. $n-1] ) { print $_->[0], "\t\t", $_->[1], "\n"; }

        Philosophy can be made out of anything. Or less -- Jerry A. Fodor

Re: Help with multidimensional sorting on arrays of hashed arrays
by Tuna (Friar) on Mar 10, 2001 at 01:06 UTC
    One thing that continues to help me in my struggle to master nested data structures is this
      Especially it's suggestion to use the debugger.
      Roughly:
      > perl -dw prog.pl Loading DB routines from perl5db.pl Enter h or `h h' for help. main::(prog.pl:3): my $firstline; DB<1> c 123 main::(prog.pl:123): %hololoh # has just been loaded w/ first value +s DB<1> x %hololoh 0 HASH(0x3e5e54) ...
      It's much easier when you can actually look at it with some initial values. (It even makes it easier to get your debugging print statements right.)

      p