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
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. | [reply] [d/l] [select] |
|
| [reply] |
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 | [reply] [d/l] |
|
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 | [reply] [d/l] [select] |
Re: sorting based on a list
by davorg (Chancellor) on May 09, 2001 at 18:16 UTC
|
#!/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
| [reply] [d/l] [select] |
|
| [reply] |
|
| [reply] [d/l] [select] |
|
@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
| [reply] |
Re: sorting based on a list
by Albannach (Monsignor) on May 09, 2001 at 18:13 UTC
|
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 | [reply] [d/l] [select] |
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"); | [reply] [d/l] |
|
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 | [reply] [d/l] [select] |
Re: sorting based on a list
by suaveant (Parson) on May 09, 2001 at 18:23 UTC
|
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 | [reply] [d/l] |
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
| [reply] [d/l] |
|
|