Sort array - subroutine help

N

Nath

I have an array of arrays:

my $output_aln_types = [
['Clustal', '.aln'],
['NEXUS', ['.nex', '.nexus']],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['User Defined', '*.*'],
];

I want to sort it so that it is alphabetical, BUT with 'NEXUS' first, and
'User Defined' last. the sort should return the following array of arrays:

my $output_aln_types = [
['NEXUS', ['.nex', '.nexus']],
['Clustal', '.aln'],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['User Defined', '*.*'],
];

i can use the following subroutine to sort alphabetically, but don't know
how to place the 2 conditions of 'NEXUS' first, and 'User Defined' last:

@$output_aln_types = sort { $a->[0] cmp $b->[0] } @$output_aln_types;


Thanks
Nathan
 
P

Paul Lalli

I have an array of arrays:

my $output_aln_types = [
['Clustal', '.aln'],
['NEXUS', ['.nex', '.nexus']],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['User Defined', '*.*'],
];

I want to sort it so that it is alphabetical, BUT with 'NEXUS' first, and
'User Defined' last. the sort should return the following array of arrays:

my $output_aln_types = [
['NEXUS', ['.nex', '.nexus']],
['Clustal', '.aln'],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['User Defined', '*.*'],
];

i can use the following subroutine to sort alphabetically, but don't know
how to place the 2 conditions of 'NEXUS' first, and 'User Defined' last:

@$output_aln_types = sort { $a->[0] cmp $b->[0] } @$output_aln_types;

untested:

sub mySort {
return -1 if $a->[0] eq 'NEXUS' or $b->[0] eq 'User Defined';
return 1 if $b->[0] eq 'NEXUS' or $a->[0] eq 'User Defined';
$a->[0] cmp $b->[0];
}

@$output_aln_types = sort mySort @$output_aln_types;



Paul Lalli
 
J

Josef Moellers

Nath said:
I have an array of arrays:

my $output_aln_types = [
['Clustal', '.aln'],
['NEXUS', ['.nex', '.nexus']],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['User Defined', '*.*'],
];
@$output_aln_types = sort { $a->[0] cmp $b->[0] } @$output_aln_types;

How about:

@$output_aln_types = sort { $a->[0] eq 'NEXUS' ? -1 : $b->[0] eq 'NEXUS'
? 1 : $a->[0] eq 'User Defined' ? 1 : $b->[0] eq 'User Defined' ? -1
: $a->[0] cmp $b->[0] } @$output_aln_types;
 
A

Anno Siegel

Nath said:
I have an array of arrays:

my $output_aln_types = [
['Clustal', '.aln'],
['NEXUS', ['.nex', '.nexus']],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['User Defined', '*.*'],
];

I want to sort it so that it is alphabetical, BUT with 'NEXUS' first, and
'User Defined' last. the sort should return the following array of arrays:

my $output_aln_types = [
['NEXUS', ['.nex', '.nexus']],
['Clustal', '.aln'],
['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
'.nt', '.aa' ] ],
['MEGA', [ '.meg', '.mega' ]],
['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
['User Defined', '*.*'],
];

i can use the following subroutine to sort alphabetically, but don't know
how to place the 2 conditions of 'NEXUS' first, and 'User Defined' last:

@$output_aln_types = sort { $a->[0] cmp $b->[0] } @$output_aln_types;

Lots of ways. You could sort using a specific comparison routine
that sorts "NEXUS" first and "User Defined" last:

@$output_aln_types = sort compare @$output_aln_types;

sub compare {
my ( $x, $y) = ( $a->[ 0], $b->[ 0]);
return -1 if $x eq 'NEXUS';
return 1 if $y eq 'NEXUS';
return 1 if $x eq 'User Defined';
return -1 if $y eq 'User Defined';
$x cmp $y;
}


Or you could exclude the special elements from the list and treat them
separately:

my ( $nexus) = grep $_->[ 0] eq 'NEXUS', @$output_aln_types;
my ( $user_defined) = grep $_->[ 0] eq 'User Defined', @$output_aln_types;
@$output_aln_types = sort { $a->[0] cmp $b->[0] }
grep { $_->[ 0] ne 'NEXUS' and $_->[ 0] ne 'User Defined' }
@$output_aln_types;
unshift @$output_aln_types, $nexus;
push @$output_aln_types, $user_defined;

....or sort the list no matter what, and move the special elements to
their places later:

@$output_aln_types = sort { $a->[0] cmp $b->[0] } @$output_aln_types;
my ( $i) = grep $output_aln_types->[ $_]->[ 0] eq 'NEXUS',
0 .. $#$output_aln_types;
unshift @$output_aln_types, splice @$output_aln_types, $i, 1;
( $i) = grep $output_aln_types->[ $_]->[ 0] eq 'User Defined',
0 .. $#$output_aln_types;
push @$output_aln_types, splice @$output_aln_types, $i, 1;

The last two solutions rely on the existence of "NEXUS" and "User Defined"
in the original list. If that can't be guaranteed, it should be checked.
The first solution doesn't care if one or both are missing.

Anno
 
U

Uri Guttman

N> I have an array of arrays:
N> my $output_aln_types = [
N> ['Clustal', '.aln'],
N> ['NEXUS', ['.nex', '.nexus']],
N> ['Phylip', [ '.phy', '.phylip', '.phlp', '.phyl', '.phy', '.ph' ]],
N> ['FASTA', [ '.fas', '.fasta', '.fast', '.seq', '.fa', '.fsa',
N> '.nt', '.aa' ] ],
N> ['MEGA', [ '.meg', '.mega' ]],
N> ['User Defined', '*.*'],
N> ];

N> I want to sort it so that it is alphabetical, BUT with 'NEXUS' first, and
N> 'User Defined' last. the sort should return the following array of arrays:

N> @$output_aln_types = sort { $a->[0] cmp $b->[0] } @$output_aln_types;

<untested>

use Sort::Maker ;

my $sorter = make_sorter( style => 'GRT', string => q{
$_->[0] eq 'NEXUS' ? '' :
$_->[0] eq 'User Defined' ? "\xff" :
$_->[0] } ) ;

my @sorted = $sorter->(@unsorted) ;

note that '' (used as the key for NEXUS) will sort before all regular
strings). and 0xff (used as the key for 'User Defined') will sort after
all regular. otherwise the regular string key is used.

this will also be much faster (if the input array size is large enough)
than the common sort block.

and it is much clearer than the other solutions which have redundant
code and you have to know when to return 1 or -1. the tradeoff is a
module and knowing how to create key values that sort before/after all
normal keys.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top