Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Remembering original order of an array

by seaver (Pilgrim)
on Sep 04, 2007 at 19:02 UTC ( [id://637004]=perlquestion: print w/replies, xml ) Need Help??

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

Dear all, I have several arrays within which the order of the elements are important. I found this easier to code than a multi-tier hash. However, when I sort one array, I would like to change the order of the other arrays likewise.

I can create a hash that stores the former index of an element, but the elements in any of the arrays are non-unique. It seems to me that I'll have to do this:

my @array=('C','B','A','C'); my %hash=(); for(my $i=0;$i<scalar(@array);$i++){ $hash{$i}=$array[$i]; } @array=sort @array; my %found=(); my @sorted_indexes=(); foreach my $k(sort {$a <=> $b } keys %hash){ for(my $i=0;$i<scalar(@array);$i++){ if($array[$i] eq $hash{$k} && !exists($found{$hash{$k}}){ print $hash{$k}."-> Former: ".$k." Latter: ".$i."\n"; $found{$hash{$k}}=1; push(@sorted_indexes,$i); } } }
The above code should give me an array which indicates, for every index, the previous index. I am sure however that there is an easier solution that I've missed, perhaps involved map, of which i've no experience.

Many thanks

Replies are listed 'Best First'.
Re: Remembering original order of an array
by ikegami (Patriarch) on Sep 04, 2007 at 19:20 UTC

    Since only unique elements are addressable through your lookup table (@sorted_indexes), why are the duplicates kept?

    When you have multiple parallel arrays, it's usually easier to just sort the indexes.

    my @names = qw( Alice Bob Eve ); my @roles = qw( sender receiver evesdropper ); my @lookup = sort { $names[$b] cmp $names[$a] } 0..$#names; for my $i (@lookup) { print("$names[$i]: $roles[$i]\n"); }

    Notice how the results are sorted (by name in descending order) while the relation between name and role isn't broken?

Re: Remembering original order of an array
by menolly (Hermit) on Sep 04, 2007 at 20:33 UTC

    You imply, but don't say explicitly, that all your arrays are the same size. Is this correct? Assuming it is, you have something like:

    @numbers = (1,2,3,4,5,6,7); @words = ('one', 'two', 'three', 'four', 'five', 'six', 'seven'); @odd_even = ('odd', 'even', 'odd', 'even', 'odd', 'even', 'odd'); @prime = ('not', 'prime', 'prime', 'not', 'prime', 'not', 'prime');
    And you want to be able to sort all of them together, based on one of them -- bringing the even numbers to the front, say?

    Have you considered an array of arrays?

    @numbers_with_attributes = ( [1, 'one', 'odd', 'not'], [2, 'two', 'even', 'prime'], [3, 'three', 'odd', 'prime'], [4, 'four', 'even', 'not'], [5, 'five', 'odd', 'prime'], [6, 'six', 'even', 'not'], [7, 'seven', 'odd', 'prime'], ); @alpha_sort = sort { $a[1] cmp $b[1] } @numbers_with_attributes;

Re: Remembering original order of an array
by CountZero (Bishop) on Sep 04, 2007 at 19:21 UTC
    If the order of the elements of your array is important, why don't you use an array in which each element is an anonymous array with its first element the value of the original element and the second element the original sequence number? That way you can always restore the original order by a simple sort on the second element of anonymous array:

    my @array=(['C',0],['B',1],['A',2],['C',3]);


    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Remembering original order of an array
by Jenda (Abbot) on Sep 05, 2007 at 07:20 UTC

    So before you find it easier to code, now you found it harder. As soon as you actually try to do anything more complex like, horrors, passing all the data to a subroutine instead of using globals, you find out that a set of unrelated related arrays is hard to handle.

    You SHOULD use an single array of hashes! Instead of

    @foo = (1,4,76,2,5,1); @bar = ('sdf', 'wrtvdf', 'erteg', 'dhrthy','rtyfgrty'); @baz = ( 'aa', 'bb', 'cc', 'dd', 'ee', 'ff');
    you should have
    @data = ( {foo => 1, bar => 'sdf', baz => 'aa'}, {foo => 4, bar => 'wrtvdf', baz => 'bb'}, {foo => 76, bar => 'erteg', baz => 'cc'}, {foo => 2, bar => 'dhrthy', baz => 'dd'}, {foo => 5, bar => 'rtyfgrty', baz => 'ee'}, {foo => 1, bar => undef, baz => 'ff'}, # did you notice we had one le +ss @bar ? );

    You can then pass this as a single entity, you can sort it using

    @sorted = sort {$a->{foo} <=> $b->{foo}} @data; #or @sorted = sort {$a->{bar} cmp $b->{bar}} @data;
    and you don't have to worry that you forget to sort one of the arrays. Imagine what you'd have to do if you found out later that you need one more array/attribute !!!

Re: Remembering original order of an array
by seaver (Pilgrim) on Sep 04, 2007 at 19:17 UTC
    I have just figured out a short solution using map:
    my $i=0; my @sorted_indexes=map {$_->[1]}sort{ $a->[0] <=> $b->[0] }map{[$_,$i+ ++]}@array; foreach my $d(@sorted_indexes){ print $d."\t".$array[$d]."\n"; }
    Thanks to all who were even beginning to consider this...:-)

      That produces a different output than your original post, but if that's what you want, it can be simplified to:

      my @sorted_indexes = sort { $array[$a] cmp $array[$b] } 0..$#array;

      Note that cmp (not <=>) should be used here since you are comparing strings (not numbers).

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://637004]
Approved by tinita
Front-paged by grinder
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (4)
As of 2024-04-15 00:35 GMT
Find Nodes?
    Voting Booth?

    No recent polls found