File Coverage

blib/lib/Sort/Key/Natural.pm
Criterion Covered Total %
statement 63 82 76.8
branch 4 16 25.0
condition 0 3 0.0
subroutine 18 19 94.7
pod 2 2 100.0
total 87 122 71.3


line stmt bran cond sub pod time code
1             package Sort::Key::Natural;
2              
3             our $VERSION = '0.04';
4              
5 1     1   825 use strict;
  1         3  
  1         41  
6 1     1   7 use warnings;
  1         2  
  1         237  
7              
8             require Exporter;
9              
10             our @ISA = qw( Exporter );
11             our @EXPORT_OK = qw( natkeysort
12             natkeysort_inplace
13             rnatkeysort
14             rnatkeysort_inplace
15             mkkey_natural
16             natsort
17             rnatsort
18             natsort_inplace
19             rnatsort_inplace
20              
21             natwfkeysort
22             natwfkeysort_inplace
23             rnatwfkeysort
24             rnatwfkeysort_inplace
25             mkkey_natural_with_floats
26             natwfsort
27             rnatwfsort
28             natwfsort_inplace
29             rnatwfsort_inplace );
30              
31              
32             require locale;
33              
34             sub mkkey_natural {
35 117 50   117 1 2474 my $nat = @_ ? shift : $_;
36 117         141 my @parts = do {
37 117 50       644 if ((caller 0)[8] & $locale::hint_bits) {
38 1     1   972 use locale;
  1         272  
  1         6  
39 1     1   1141 $nat =~ /\d+|\p{IsAlpha}+/g;
  1         12  
  1         16  
  0         0  
40             }
41             else {
42 117         589 $nat =~ /\d+|\p{IsAlpha}+/g;
43             }
44             };
45 117         278 for (@parts) {
46 216 100       619 if (/^\d/) {
47 81         104 s/^0+//;
48 81         98 my $len = length;
49 81         122 my $nines = int ($len / 9);
50 81         111 my $rest = $len - 9 * $nines;
51 81         391 $_ = ('9' x $nines) . $rest . $_;
52             }
53             }
54 117         1788 return join("\0", @parts);
55             }
56              
57 1     1   35598 use Sort::Key::Register natural => \&mkkey_natural, 'string';
  1         4  
  1         9  
58 1     1   6 use Sort::Key::Register nat => \&mkkey_natural, 'string';
  1         2  
  1         5  
59              
60 1     1   582 use Sort::Key::Maker natkeysort => 'nat';
  1         3  
  1         6  
61 1     1   6 use Sort::Key::Maker rnatkeysort => '-nat';
  1         2  
  1         5  
62 1     1   7 use Sort::Key::Maker natsort => \&mkkey_natural, 'str';
  1         2  
  1         6  
63 1     1   6 use Sort::Key::Maker rnatsort => \&mkkey_natural, '-str';
  1         2  
  1         5  
64              
65             sub mkkey_natural_with_floats {
66 0 0   0 1   my $nat = @_ ? shift : $_;
67 0           my @parts = do {
68 0 0         if ((caller 0)[8] & $locale::hint_bits) {
69 1     1   7 use locale;
  1         1  
  1         8  
70 0           $nat =~ /[+\-]?\d+(?:\.\d*)?|\p{IsAlpha}+/g;
71             }
72             else {
73 0           $nat =~ /[+\-]?\d+(?:\.\d*)?|\p{IsAlpha}+/g;
74             }
75             };
76 0           for (@parts) {
77 0 0         if (my ($sign, $number, $dec) = /^([+-]?)(\d+)(?:\.(\d*))?$/) {
78 0           $number =~ s/^0+//;
79 0 0         $dec = '' unless defined $dec;
80 0           $dec =~ s/0+$//;
81 0           my $len = length $number;
82 0           my $nines = int ($len / 9);
83 0           my $rest = $len - 9 * $nines;
84 0           $_ = ('9' x $nines) . $rest . $number . $dec;
85 0 0 0       if ($sign eq '-' and $_ ne '0') {
86 0           tr/0123456789/9876543210/;
87 0           $_ = "-$_";
88             }
89             }
90             }
91 0           return join("\0", @parts);
92             }
93              
94 1     1   3705 use Sort::Key::Register natural_with_floats => \&mkkey_natural_with_floats, 'string';
  1         4  
  1         16  
95 1     1   6 use Sort::Key::Register natwf => \&mkkey_natural_with_floats, 'string';
  1         2  
  1         4  
96              
97 1     1   6 use Sort::Key::Maker natwfkeysort => 'natwf';
  1         4  
  1         7  
98 1     1   5 use Sort::Key::Maker rnatwfkeysort => '-natwf';
  1         2  
  1         4  
99 1     1   5 use Sort::Key::Maker natwfsort => \&mkkey_natural_with_floats, 'str';
  1         2  
  1         50  
100 1     1   7 use Sort::Key::Maker rnatwfsort => \&mkkey_natural_with_floats, '-str';
  1         3  
  1         5  
101              
102              
103             1;
104              
105             =head1 NAME
106              
107             Sort::Key::Natural - fast natural sorting
108              
109             =head1 SYNOPSIS
110              
111             use Sort::Key::Natural qw(natsort);
112              
113             my @data = qw(foo1 foo23 foo6 bar12 bar1
114             foo bar2 bar-45 foomatic b-a-r-45);
115              
116             my @sorted = natsort @data;
117              
118             print "@sorted\n";
119             # prints:
120             # b-a-r-45 bar1 bar2 bar12 bar-45 foo foo1 foo6 foo23 foomatic
121              
122             use Sort::Key::Natural qw(natkeysort);
123              
124             my @objects = (...);
125             my @sorted = natkeysort { $_->get_id } @objects;
126              
127              
128             =head1 DESCRIPTION
129              
130             This module extends the L family of modules to support
131             natural sorting.
132              
133             Under natural sorting, strings are split at word and number
134             boundaries, and the resulting substrings are compared as follows:
135              
136             =over 4
137              
138             =item *
139              
140             numeric substrings are compared numerically
141              
142             =item *
143              
144             alphabetic substrings are compared lexically
145              
146             =item *
147              
148             numeric substrings come always before alphabetic substrings
149              
150             =back
151              
152             Spaces, symbols and non-printable characters are only considered for
153             splitting the string into its parts but not for sorting. For instance
154             C is broken in three substrings C, C and C<42>
155             and after that the dashes are ignored.
156              
157             Note, that the sorting is case sensitive. To do a case insensitive
158             sort you have to convert the keys explicitly:
159              
160             my @sorted = natkeysort { lc $_ } @data
161              
162             Also, once this module is loaded, the new type C (or C) will
163             be available from L. For instance:
164              
165             use Sort::Key::Natural;
166             use Sort::Key::Maker i_rnat_keysort => qw(integer -natural);
167              
168             creates a multi-key sorter C accepting two keys, the
169             first to be compared as an integer and the second in natural
170             descending order.
171              
172             There is also an alternative set of natural sorting functions that
173             recognize floating point numbers. They use the key type C
174             (abbreviation of C).
175              
176             =head2 FUNCTIONS
177              
178             the functions that can be imported from this module are:
179              
180             =over 4
181              
182             =item natsort @data
183              
184             returns the elements of C<@data> sorted in natural order.
185              
186             =item rnatsort @data
187              
188             returns the elements of C<@data> sorted in natural descending order.
189              
190             =item natkeysort { CALC_KEY($_) } @data
191              
192             returns the elements on C<@array> naturally sorted by the keys
193             resulting from applying them C.
194              
195             =item rnatkeysort { CALC_KEY($_) } @data
196              
197             is similar to C but sorts the elements in descending
198             order.
199              
200             =item natsort_inplace @data
201              
202             =item rnatsort_inplace @data
203              
204             =item natkeysort_inplace { CALC_KEY($_) } @data
205              
206             =item rnatkeysort_inplace { CALC_KEY($_) } @data
207              
208             these functions are similar respectively to C, C,
209             C and C, but they sort the array C<@data> in
210             place.
211              
212             =item $key = mkkey_natural $string
213              
214             given C<$string>, returns a key that can be compared lexicographically
215             to another key obtained in the same manner, results in the same order
216             as comparing the former strings as in the natural order.
217              
218             If the argument C<$key> is not provided it defaults to C<$_>.
219              
220             =item natwfsort @data
221              
222             =item rnatwfsort @data
223              
224             =item natwfkeysort { CALC_KEY($_) } @data
225              
226             =item rnatwfkeysort { CALC_KEY($_) } @data
227              
228             =item natwfsort_inplace @data
229              
230             =item rnatwfsort_inplace @data
231              
232             =item natwfkeysort_inplace { CALC_KEY($_) } @data
233              
234             =item rnatwfkeysort_inplace { CALC_KEY($_) } @data
235              
236             =item mkkey_natural_with_floats $key
237              
238             this ugly named set of functions perform in the same way as its
239             s/natwf/nat/ counterpart with the difference that they honor floating
240             point numbers embedded inside the strings.
241              
242             In this context a floating point number is a string matching the
243             regular expression C. Note that numbers with an
244             exponent part (i.e. C<1.12E-12>) are not recognized as such.
245              
246             Note also that numbers without an integer part (i.e. C<.2> or C<-.12>)
247             are not supported either.
248              
249             =back
250              
251             =head1 SEE ALSO
252              
253             L, L.
254              
255             Other module providing similar functionality is L.
256              
257             =head1 COPYRIGHT AND LICENSE
258              
259             Copyright (C) 2006, 2012, 2014 by Salvador FandiEo,
260             Esfandino@yahoo.comE.
261              
262             This library is free software; you can redistribute it and/or modify
263             it under the same terms as Perl itself, either Perl version 5.8.4 or,
264             at your option, any later version of Perl 5 you may have available.
265              
266             =cut