Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

sorting based on a list

by mbond (Beadle)
on May 09, 2001 at 18:05 UTC ( [id://79086]=perlquestion: print w/replies, xml ) Need Help??

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

I have a list, lets call it:

@list = qw(a z b y c x);

and i have another list ... lets call this one:

@list2 = qw(zulu charlie xray yankee bravo alpha);

here's my problem:

I want to sort @list2 based on the order in @list, is there an easy way to do this using the sort function, or do i just have to loop through it like:
foreach $a (@list) { foreach (@list2) { push(@list3,$_) if substr($_,0,1) eq "$a"; } }

thanks,

mbond

Replies are listed 'Best First'.
Re: sorting based on a list
by merlyn (Sage) on May 09, 2001 at 18:36 UTC
    my @indicies = sort { $list[$a] cmp $list[$b] } 0..$#list; my @rearranged = @list2[@indicies];

    -- Randal L. Schwartz, Perl hacker


    update: Hold on. I solved the problem he stated. Not the problem he had. OK, nothing cannot be solved without a bit of indirection:
    my @indicies = sort { $list[$a] cmp $list[$b] } 0..$#list; my @rearranged; @rearranged[@indicies] = sort @list2;
    There. Make a sorted @list2 be disordered in the same way that @list is.

    Also see my other solution.

      Umm.. this returns zulu xray bravo alpha yankee charlie

      that's not ordered right...

      Update Yup, all better now
                      - Ant

Re: sorting based on a list
by merlyn (Sage) on May 09, 2001 at 19:23 UTC
    Hmm. I just realized there's another interpretation of the question that might be more valid than my other solution.

    Suppose the question means: given elements of @list2 which begin with the letters of @list, rearrange them so that the beginning letters are sorted in that order. Thus, the length of @list2 in no way correlates with the length of @list, and this was merely a concidence in the sample set.

    To that, I'll have to add two arbitrary decisions: if the word doesn't begin with one of the letters, put it first, and within a given initial letter, sort alphabetically.

    The code for that would look like this:

    my %mapping; @mapping{@list} = 1..@list; my @rearranged = map { $_->[0] } sort { $a->[1] <=> $b->[1] or $a->[0] cmp $b->[0] } map { [$_, $mapping{substr($_,0,1)} || 0] } @list2;
    There. One of them there, Schwartzian thingies.

    And yes, I see other similar solutions in this thread, but I bet those repeated index() calls inside the sort comparator are gonna make it doggy slow for dozens of elements. By caching the reverse map, as well as the sort position, we save mondo time.

    -- Randal L. Schwartz, Perl hacker

      And here, for completeness, is a (presumably much slower) solution based on the opposite assumptions (extra letters last, and continue the comparison down the string). This is, of course, more or less equivalent to rewriting cmp with your own alphabet. It could undoubtedly be done faster with an iterative solution, but then it could also be done faster in C.

      use strict; use vars '$sortstr'; my @list = qw(a z b y c x); my @list2 = qw(zulu zuzu zucu charlie fred xray yankee bravo alpha); $sortstr = join('', @list); my @sorted_list = sort mycmp @list2; sub mycmp { my ($A, $B)= @_ ? @_ : ($a,$b); my $temp; my $Aspot = ($temp = index($sortstr,substr($A,0,1))) < 0 ? length($sortstr) : $temp; my $Bspot = ($temp = index($sortstr,substr($B,0,1))) < 0 ? length($sortstr) : $temp; return $Aspot <=> $Bspot || mycmp(substr($A,1),substr($B,1)) || $A cmp $B }

      Sorts to:
      alpha zuzu zucu zulu bravo yankee charlie xray fred



      If God had meant us to fly, he would *never* have give us the railroads.
          --Michael Flanders

Re: sorting based on a list
by davorg (Chancellor) on May 09, 2001 at 18:16 UTC

    Here's one suggestion using hash slices:

    #!/usr/bin/perl -w use strict; my @list = qw(a z b y c x); my @list2 = qw(zulu charlie xray yankee bravo alpha); my %hash; @hash{@list} = @list2; my @sorted = @hash{sort @list}; print "@sorted\n";

    Update: OK. Misunderstood the question. In that case it's:

    @hash{sort @list} = sort @list2; my @sorted = @hash{@list};

    but merlyn's solution is better :(

    --
    <http://www.dave.org.uk>

    "Perl makes the fun jobs fun
    and the boring jobs bearable" - me

      This doesn't work... he wants to sort the words in order of their first letters based on the @list array... your thing assigns the characters to the items in list 2, then orders that list and reads them, returning...

      zulu xray bravo alpha yankee charlie
      instead of
      alpha zulu bravo yankee charlie xray
                      - Ant

      what's @hash{@list}?? and wouldn't hasing it up kill duplicates?? wouldn't someone need those duplicates??

      more: I ran this and got: zulu xray bravo alpha yankee charlie is this considered sorted??


      He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.

      Chady | http://chady.net/

        @hash{@list} is the list of hash values corresponding to the keys in @list.

        And, yes, it does break if @list contains duplicates.

        --
        <http://www.dave.org.uk>

        "Perl makes the fun jobs fun
        and the boring jobs bearable" - me

Re: sorting based on a list
by Albannach (Monsignor) on May 09, 2001 at 18:13 UTC
    How about:

    use strict; use warnings; my $sortorder = 'azbycx'; my @list2 = qw(zulu charlie xray yankee bravo alpha); print join ',', @list2,"\n"; print join ',', sort {index($sortorder,substr($a,0,1)) <=> index($sort +order,substr($b,0,1))} @list2
    Update: Me stupid! This only sorts by the first character so it won't re-order qw(zvlu zulu) correctly (and I should have added a call to lc around the substr)! Note that alfie fixed the former bug here, if you add ChemBoy's fix (<=> to cmp) to that ;-).

    --
    I'd like to be able to assign to an luser

Re: sorting based on a list
by alfie (Pilgrim) on May 09, 2001 at 18:19 UTC
    If you are really going to sort by the first letter only in a special order you might try it with this:
    my $sortstr = join('', @list); my @sorted_list = sort { index($sortstr,substr($a,0,1)) <=> index($sortstr,substr($b,0,1)) || $a <=> $b } @list2;
    The first will compare the first letter only, the later will compare if the first letter is equal.
    --
    use signature; signature(" So long\nAlfie");

      Nice idea--I propose the following modification, to dump extra words at the end (and to use string comparison, of course):

      my $sortstr = join('', @list); my @sorted_list = sort { rindex($sortstr,substr($b,0,1)) <=> rindex($sortstr,substr($a,0,1)) || $a cmp $b } @list2;

      Of course, you might want words that start with letters not in the list to be at the front, in which case, alphie's solution is preferable (aside from using <=> instead of cmp).

      Update: doh! It doesn't do what I thought it did! Fix coming...
      Fix here:

      my $sortstr = join('', @list); my @sorted_list = sort { my ($tmp1,$tmp2); (($tmp1 = index($sortstr,substr($a,0,1))) >= 0 ? $tmp1 : length($sort +str)) <=> (($tmp2 = index($sortstr,substr($b,0,1))) >= 0 ? $tmp2 : length($sort +str)) || $a cmp $b } @list2;



      If God had meant us to fly, he would *never* have give us the railroads.
          --Michael Flanders

Re: sorting based on a list
by suaveant (Parson) on May 09, 2001 at 18:23 UTC
    you could also do...
    my(%order); @order{qw(a z b y c x)} = (0..5); for(sort { $order{substr($a,0,1)} <=> $order{substr($b,0,1)} } @list2) + {...
    I doubt this is faster than Albannach's, but it is another way.
                    - Ant
Re: sorting based on a list
by SilverB1rd (Scribe) on May 09, 2001 at 18:20 UTC
    I would suggest using a hash for that. You can then sort the hash or pull the words out by letter.
    my %foo = { 'a' => 'alpha', 'b' => 'bravo', 'c' => 'charlie'}; foreach $x (sort(keys(%foo))) { print $foo{$x}\n; } print $foo{'b'},$foo{'a'},$foo{'c'};


    ------
    The Price of Freedom is Eternal Vigilance

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://79086]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-04-25 16:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found