Sort array - subroutine help

Discussion in 'Perl Misc' started by Nath, Aug 16, 2004.

  1. Nath

    Nath Guest

    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
    Nath, Aug 16, 2004
    #1
    1. Advertising

  2. Nath

    Paul Lalli Guest

    On Mon, 16 Aug 2004, Nath wrote:

    > 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
    Paul Lalli, Aug 16, 2004
    #2
    1. Advertising

  3. Nath wrote:
    > 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;

    --
    Josef Möllers (Pinguinpfleger bei FSC)
    If failure had no penalty success would not be a prize
    -- T. Pratchett
    Josef Moellers, Aug 16, 2004
    #3
  4. Nath

    Anno Siegel Guest

    Nath <DON'T_SEND_ME@TRIPE_TO_MY_IN.BOX> wrote in comp.lang.perl.misc:
    > 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
    Anno Siegel, Aug 16, 2004
    #4
  5. Nath

    Uri Guttman Guest

    >>>>> "N" == Nath <DON'T_SEND_ME@TRIPE_TO_MY_IN.BOX> writes:

    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
    Uri Guttman, Aug 16, 2004
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Casey
    Replies:
    3
    Views:
    841
    Casey
    Jan 30, 2004
  2. Navin
    Replies:
    1
    Views:
    657
    Ken Schaefer
    Sep 9, 2003
  3. GIMME
    Replies:
    5
    Views:
    173
    Thomas 'PointedEars' Lahn
    Jul 26, 2004
  4. Kirk Is
    Replies:
    5
    Views:
    90
    gnari
    Nov 26, 2004
  5. king
    Replies:
    5
    Views:
    169
Loading...

Share This Page