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

I have created many large, complex, and ultimately problematic data structures. I am trying to find a way to index these structures to make for quicker and easier searching. I would like to start by asking your help with a fairly simple one. Hopefully I'll learn enough to tackle the others myself. Here is an example of a structure:
$a[1]{total} = 10; $a[2]{total} = 50; $a[3]{total} = 40; $a[4]{total} = 30; $a[5]{total} = 20;
I would to end up with a new array that holds the indexes of the @a array sorted by the values for the hash key total. For example:
$b[1] == 2; #$a[2]{total} = 50 $b[2] == 3; #$a[3]{total} = 40 $b[3] == 4; #$a[4]{total} = 30 $b[4] == 5; #$a[5]{total} = 20 $b[5] == 1; #$a[1]{total} = 10
I just can't seem to get a grip on this one.

Replies are listed 'Best First'.
Re: Indexing a data structure
by Roger (Parson) on Oct 16, 2003 at 03:12 UTC
    What you are looking for is to store the rank of elements in @a in @b. (I think it was Randal who came up with the following idea first) -

    use Data::Dumper; $a[1]{total} = 10; $a[2]{total} = 50; $a[3]{total} = 40; $a[4]{total} = 20; $a[5]{total} = 30; @b[sort{$a[$b]{total}<=>$a[$a]{total}}1..$#a] = 1..$#a; print Dumper(\@b);
    And the output is -

    $VAR1 = [ undef, # this is ignored because index begins at 1 5, 1, 2, 4, 3 ];
Re: Indexing a data structure
by Fletch (Bishop) on Oct 16, 2003 at 02:13 UTC

    Fish.

    my @b = map { $_->[1] } sort { $b->[0] <=> $a->[0] } map { [ $a[ $_ ]->{total}, $_ ] } 0..$#a;

    Update: Frell, left off the subscript inside the sort block.

    $a[0]->{total} = 0; $a[1]->{total} = 10; $a[2]->{total} = 50; $a[3]->{total} = 40; $a[4]->{total} = 30; $a[5]->{total} = 20; @b = map { $_->[1] } sort { $b->[0] <=> $a->[0] } map { [ $a[$_]->{total}, $_ ] } 1..$#a; print "$b[$_] = $a[$b[$_]]->{total}\n" for 0..$#b;
      I tried:
      #!c:\perl\bin\perl.exe -w use strict; use warnings; use diagnostics; my @a; $a[1]{total} = 10; $a[2]{total} = 50; $a[3]{total} = 40; $a[4]{total} = 30; $a[5]{total} = 20; my @b = map { $_->[1] } sort { $b <=> $a } map { [ $a[ $_ ]->{total}, $_ ] } 0..$#a; for my $x (0..$#b) { print "$b[$x] = $a[$b[$x]]{total}\n"; }
      And the output was:
      5 = 20 4 = 30 3 = 40 2 = 50 1 = 10 Use of uninitialized value in concatenation (.) or ... 0 =
      I understand the waring since the code refers to element 0 of the array @a when it was never defined. However the output is not what I expected. I was looking for:
      2 = 50 3 = 40 4 = 30 5 = 20 1 = 10
        I think the solution is actually simpler than what Fletch suggested:
        #!/usr/bin/perl use strict; use warnings; use diagnostics; my @ary; # let's avoid confusion with the $a and $b used by sort() $ary[0]{total} = 5; # let's avoid those silly warnings $ary[1]{total} = 10; $ary[2]{total} = 50; $ary[3]{total} = 40; $ary[4]{total} = 30; $ary[5]{total} = 20; my @bry = sort { $ary[$b]{total} <=> $ary[$a]{total} } 0..$#ary; for my $x (0..$#bry) { print "$bry[$x] = $ary[$bry[$x]]{total}\n"; } __OUTPUT__ 2 = 50 3 = 40 4 = 30 5 = 20 1 = 10 0 = 5
      Elegant solution and it works, but you don't really have to use a Schwartzian transformation though. A simple sort would be sufficient in this case.

        Yeah, but . . . but . . . this one goes to 11.

        Actually I think I remembered the Randal version and hence thought it must need the transform in there somewhere. Yeah, that's the ticket. :)

Re: Indexing a data structure
by princepawn (Parson) on Oct 16, 2003 at 02:34 UTC
    throw your data into DBD::SQLite and then slice and dice as you please with SQL :)

    Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality.