Perl 数字排序:如何忽略前导字母字符



我有一个 1,660 行数组,如下所示:

...
H00504
H00085
H00181
H00500
H00103
H00007
H00890
H08793
H94316
H00217
...

而且主角永远不会改变。它总是"H"然后是五位数字。但是当我在Perl中做我认为是数字排序的事情时,我得到了奇怪的结果。某些段按顺序排序,但随后启动不同的段。这是排序后的细分:

...
H01578
H01579
H01580
H01581
H01582
H01583
H01584
H00536
H00537
H00538
H01585
H01586
H01587
H01588
H01589
H01590
...

我正在尝试的是:

my @sorted_array = sort {$a <=> $b} @raw_array;

但显然它不起作用。有人知道为什么吗?

我应该补充一点,尽管这些值都以"H"为前缀,但将来我们仍然有可能最终会得到以其他字母为前缀的其他数据。这意味着删除 H、排序然后替换 H 不是解决方案

如果你按照你应该的方式使用use strict; use warnings;,你会得到许多形式的错误

Argument "H01578" isn't numeric in numeric comparison (<=>)

你的元素都不是数字,所以它们都被认为是零。这就是为什么 Perl 认为当前代码的结果是排序的。


如果要按字母排序,请按数字排序

(这与仅按数字排序相同,因为所有字母都相同):

my @sorted_array = sort @raw_array;

这是

my @sorted_array = sort { $a cmp $b } @raw_array;

如果要按数字排序而不考虑前导字母,请改用以下内容:

my @sorted_array =
   sort { substr($a, 1) <=> substr($b, 1) }
    @raw_array;

如果要按初始字符作为主键排序,然后按数字作为辅助键进行排序,则可以使用施瓦茨变换的变体,该变换在排序之前从所有数据中提取两个字段进行比较。

此程序演示

use strict;
use warnings;
my @data = <DATA>;
chomp @data;
my @sorted = sort map $_->[0],
sort { $a->[1] cmp $b->[1] or $a->[2] <=> $b->[2] }
map [$_, /(.)(.+)/], @data;
print "$_n" for @sorted;
__DATA__
A1180
B0802
B1284
C0899
C1455
C0765
A1207
A0909
C0921
C1060
A1067
B1486
A1268
B0772
C0595
B0734
A1004
A0607
A1323
B1181

输出

A0607
A0909
A1004
A1067
A1180
A1207
A1268
A1323
B0734
B0772
B0802
B1181
B1284
B1486
C0595
C0765
C0899
C0921
C1060
C1455

工具成功完成

您可能更喜欢不使用转换的替代方法。该程序具有相同的输出,但对于大型数据集,运行速度会慢得多

my @sorted = sort {
  my @a = $a =~ /(.)(.+)/;
  my @b = $b =~ /(.)(.+)/;
  $a[0] cmp $b[0] or $a[1] <=> $b[1];
} @data;

要忽略任何不是数字的内容,您还可以:

use strict;
use warnings;
my @sorted =    sort {
                        (my $x = $a) =~ s/D//g;
                        (my $y = $b) =~ s/D//g;
                        ($x?$x:0) <=> ($y?$y:0)
                } <DATA>;
print "$_" for @sorted;
__DATA__
a123
a/9999/gyu
b2
333
bbb
c888hh
0

结果:

bbb
0
b2
a123
333
c888hh
a/9999/gyu

你想要这样的东西:

my @sorted_array = sort {substr($a, 1) <=> substr($b,1)} @raw_array;

有关示例,请参阅:http://ideone.com/trnfy。

如果您使用标准排序,而不使用{...},那也应该有效。 您当前的代码可能失败,因为所有比较都返回 0,因为您正在对字母数字数据进行数字比较。

您可以使用List::UtilsBy::sort_by来避免施瓦茨变换和其他相关噪声:

use List::UtilsBy qw( sort_by );
my @sorted_array = sort_by { substr($_, 1) } @raw_array;

最新更新