Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

How do I do a natural sort on an array?

( #68185=categorized question: print w/ replies, xml ) Need Help??
Contributed by Anonymous Monk on Mar 30, 2001 at 00:46 UTC
Q&A  > sorting


Description:

How do I do a natural sort on an array? i.e. each array element is contains a string made up of numbers and letters.

Answer: How do I do a natural sort on an array?
contributed by tye

my @sorted= grep {s/(^|\D)0+(\d)/$1$2/g,1} sort grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @data;
See RE: RE: Re: Sorting on Section Numbers some important style comments.

Features and limitations:

  • Strips extra leading zeros from digit strings
  • Doesn't handle floating point numbers at all well
  • Sorts negative integers in reverse after positive integers
  • Uses very little extra memory
  • You must specify the maximum number of digits your integers will have (6 in the above code)
Answer: How do I do a natural sort on an array?
contributed by tye

my %data; foreach my $data ( @data ) { ( my $sort= $data ) =~ s/(0*)(\d+)/ pack("C",length($2)) . $1 . $2 /ge; $data{$sort}= $data; } my @sorted= @data{ sort keys %data };
Bugs and features
  • Doesn't properly sort decimal values like 12.34
  • Sorts negative integers in reverse after positive integers
  • Requires more memory than other methods I'll be adding
Answer: How do I do a natural sort on an array?
contributed by indigo

@a = sort @b;

will sort lexigraphically.

@a = sort { $a <=> $b } @b;

will sort numerically.

@a = sort &naturally @b;

will sort "naturally", where naturally() is a compare routine of your own devising.

perldoc -f sort for more info.

Answer: How do I do a natural sort on an array?
contributed by Dominus

I'm not exactly sure what you want (it would have helped if you had provided an example) but here's what I use:

sub byfile { my @a = split /(\d+)/, $a; my @b = split /(\d+)/, $b; my $M = @a > @b ? @a : @b; my $res = 0; for (my $i = 0; $i < $M; $i++) { return -1 if ! defined $a[$i]; return 1 if ! defined $b[$i]; if ($a[$i] =~ /\d/) { $res = $a[$i] <=> $b[$i]; } else { $res = $a[$i] cmp $b[$i]; } last if $res; } $res; }
This may be more complicated than you need. Given the following:
53 7 119 53red red5 red6 red7 red67 red6.jpg red12.jpg green4.jpg blue2.jpg blue1000.jpg blue2.jpg58
it produces the following output:
7 53 53red 119 blue2.jpg blue2.jpg58 blue1000.jpg green4.jpg red5 red6 red6.jpg red7 red12.jpg red67
Lucs St. Louis also suggests:
while (defined (my $A = shift @a) and defined (my $B = shift @b)) { $res = ($A =~ /\d/) ? $A <=> $B : $A cmp $B; return $res if $res; } return defined $A ? -1 : 1;
Hope this helps.
Answer: How do I do a natural sort on an array?
contributed by salva

Sort::Key::Natural is fast, can handle numbers of unlimited size and doesn't have problems with unicode:

use Sort::Key::Natural qw(natsort); my @sorted = natsort @data;
Answer: How do I do a natural sort on an array?
contributed by ihb

You can use Sort::Naturally.

Answer: How do I do a natural sort on an array?
contributed by Jammerwoch

I wrote the following before finding the delightful Sort::Naturally library function. I reccommend using that over what I've done, but maybe people are curious how it might be done. Or maybe someone needs to fine-tune or customize it.

The following code is NOT efficient. In particular, the calling of naturalSortInner with $a and $b as arguments completely eliminates the efficiency of having $a and $b. However, it was necessary for the recursive nature of naturalSortInner.

#!/usr/bin/perl sub naturalSortInner { $x = uc( shift ); $y = uc( shift ); if( !($x =~ /\d+(\.\d+)?/) ) { return $x cmp $y; } $xBefore = $`; $xMatch = $&; $xAfter = $'; if( !($y =~ /\d+(\.\d+)?/) ) { return $x cmp $y; } if( $xBefore eq $` ) { if( $xMatch == $& ) { return naturalSortInner( $xAfter, $' ); } else { return $xMatch <=> $&; } } else { return $x cmp $y; } print "\n<before: '$xBefore', match: '$xMatch', after: '$xAfter'>\ +n"; } sub naturalSort { naturalSortInner( $a, $b ); } @arr = ( 'beta', 'Alpha', 'Gamma1', 'Gamma', '23', '5', 'Version1', 'Version1.1', 'Version1.2', 'Version11.1-Sub1', 'Version11.1-Sub10', 'Version11.1-Sub3', 'x23sub5', 'Version2', 'Version2.1', 'GammaGlobulin', 'Gamma10', 'c', 'Gamma2', 'Gamma3', ); print join( "\n", sort naturalSort @arr ) . "\n";

Please (register and) log in if you wish to add an answer



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others making s'mores by the fire in the courtyard of the Monastery: (9)
    As of 2015-07-31 05:42 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









      Results (274 votes), past polls