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

Hello, i'm looking for elegant solution how to sort an array and "sort that way" associated array . For example let's have array
@array_items = {"dog" ,"desk" ,"cow"}
@array_items_weight = {"20" ,"10", "150"}
my desired output after sorting would like like:
@array_items = {"cow" ,"desk" ,"dog"}
@array_items_weight = {"150" ,"10" ,"20"}
Any ideas? thanks a lot
  • Comment on Sort array + keep associated indexes in other array

Replies are listed 'Best First'.
Re: Sort array + keep associated indexes in other array
by GrandFather (Saint) on Mar 28, 2012 at 09:07 UTC

    Simple, only sort one thing. Maintaining parallel data structures is a real pain, so don't do that. Instead use either an array of arrays or a hash. Consider:

    use strict; use warnings; my @items = (["dog", 20], ["desk", 10], ["cow", 150]); print "By name:\n"; print "$_->[0]: $_->[1]\n" for sort {$a->[0] cmp $b->[0]} @items; print "\nBy weight:\n"; print "$_->[0]: $_->[1]\n" for sort {$a->[1] <=> $b->[1]} @items;

    and:

    use strict; use warnings; my %items = (dog => 20, desk => 10, cow => 150); print "By name:\n"; print "$_: $items{$_}\n" for sort {$a cmp $b} keys %items; print "\nBy weight:\n"; print "$_: $items{$_}\n" for sort {$items{$a} <=> $items{$b}} keys %it +ems;

    Both print:

    By name: cow: 150 desk: 10 dog: 20 By weight: desk: 10 dog: 20 cow: 150
    True laziness is hard work
      yes I was thinking that way too, but I'm trying to avoid that solution, because in my script it's not very easy to join those two arrays ( there are variable count of spaces between them during printing, the arrays are filled in different time etc.). That sorting i'm trying to do is the last thing in my script (I will only print it afterwards), so I don't have to worry about maintaining parallel data structures

        In that case, you can zip the arrays together before sorting them, then unzip them afterwards.

        Something like...

        use Data::Dumper; my @array_items = ("dog" ,"desk" ,"cow"); my @array_items_weight = ("20" ,"10", "150"); # zip them together my @combined = map { [$array_items[$_], $array_items_weight[$_]] } 0 .. $#array_items; # sort the zipped array my @sorted = sort { $a->[0] cmp $b->[0] } @combined; # unzip again @array_items = map { $_->[0] } @sorted; @array_items_weight = map { $_->[1] } @sorted; print "\@array_items is now... "; print Dumper \@array_items; print "\@array_items_weight is now... "; print Dumper \@array_items_weight;

        The above produces the following output...

        @array_items is now... $VAR1 = [
                  'cow',
                  'desk',
                  'dog'
                ];
        @array_items_weight is now... $VAR1 = [
                  '150',
                  '10',
                  '20'
                ];
        

        This solution assumes that @array_items and @array_items_weight are equal in length.

        Zipping them together is somewhat ugly code, but with the assistance of List::MoreUtils it can be made more beautiful:

        my @combined = pairwise { [$a, $b] } @array_items, @array_items_weight +;
        perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: Sort array + keep associated indexes in other array
by johngg (Canon) on Mar 28, 2012 at 13:21 UTC

    Sort the indices then use an array slice on each array.

    knoppix@Microknoppix:~$ perl -E ' > @items = qw{ dog desk cow }; > @weights = qw{ 20 10 150 }; > @sortedIdx = sort { $items[ $a ] cmp $items[ $b ] } 0 .. $#items; > @items = @items[ @sortedIdx ]; > @weights = @weights[ @sortedIdx ]; > say qq{@items}; > say qq{@weights};' cow desk dog 150 10 20 knoppix@Microknoppix:~$

    I hope this is helpful.

    Cheers,

    JohnGG

Re: Sort array + keep associated indexes in other array
by Eliya (Vicar) on Mar 28, 2012 at 13:17 UTC

    You can compute an appropriately sorted list of indices, and then reorder both arrays using those indices:

    #!/usr/bin/perl -l my @array_items = ("dog", "desk" ,"cow"); my @array_items_weight = ("20" , "10", "150"); my @i = sort { $array_items[$a] cmp $array_items[$b] } 0..$#array_item +s; @array_items = @array_items[@i]; @array_items_weight = @array_items_weight[@i]; print "@array_items"; # cow desk dog print "@array_items_weight"; # 150 10 20
Re: Sort array + keep associated indexes in other array
by Anonymous Monk on Mar 29, 2012 at 13:13 UTC
    Consider building a list or hash of structures that are strictly for the purpose of sorting, containing references (or undef as the case may be) to the detailed records.   Your list might therefore be as in your example [ {name=>"cow", weight_record=>some_reference}, ... ].   A hash structure might be handiest.   This structure exists simply for the purpose of, and when, you are preparing the final sorted output.   It contains references to the data that will go into that output.   It is structured in whatever way is conducive to the purpose of arranging the output, drawing its data from other existing data structures which remain conducive to the data-preparation operations which preceded it.