如何在Perl中对字母数字键进行排序?

时间:2015-07-14 16:28:45

标签: linux perl

我有一个哈希,其键看起来像AA00,AA01,AB00,AB23,ZA03,ZB45,AA02,DA05,AA45,DE67,DE84,ZZ99,我需要先按字母设置然后按照次要编号对它们进行排序到专业。

编辑:案件确实更复杂。必须从左到右阅读字母,并且数字和字母的数量可能会改变。也就是说,A00必须在AA00和AB00之前。 B00必须在AZ99之后但在BA00之前。

此外,如果我找到AA,AAA,AAB和AAAA,则AAA,AAB应被视为AA之前的子集,AAAA应位于三字母子集之前。但是,ABAA必须在AA之后。

数字是线性顺序,即0在1之前,1在99之前,但是数字没有限制。 1可以用1或01表示(见ZB)。忽略空格,它们用于维护列。

即,

A   00,
AAAA00,
AAA 00,
AAB 00,
AA  00,
AA  01,
AA  02,
AA  45,
ABAA00,
AB  00, (first letter change)
AB  23,
AZ  99,
B   00,
BA  00,
DA  05, (second letter change, first letter restarts as A)
DE  67,
DE  84,
ZA  03,
ZB  45,
ZB 145,
ZB1145,
ZZ  99,

我尝试了经典

for $key ( sort {$a<=>$b} keys %hash) {
       print "($key)->($hash{$key})\n";
}

但根本没有产生任何分类。确实,钥匙完全没有通过。

有用于生成数据集的逻辑。他们使用了:

 While (Certain thing is True) {
   Select a $letter; 
   $identifier .= $letter 
 } 

假设第一个字母是a表示集合A,第二个字母表示A中的子集。也就是说,如果我有AA,AB和AC,则A,B和C是A中的子集。我有ABC,C是集合A​​的子集B中的一个子集。如果我有,那么,ABCA,最后一个A是C中的一个子集。

2 个答案:

答案 0 :(得分:4)

my @sorted =
   map $_->[0],
   sort {
      $a->[1] cmp $b->[1]
         ||
      $a->[2] <=> $b->[2]
   }
   map {
      my ($l, $n) = /^([A-Z]+)([0-9]{1,9})\z/
         or die("Unexpected data");

      $l .= "\xFF" if length($l) > 1;

      [ $_, $l, $n ]
   }
   @unsorted;

优化

my @sorted =
   map { unpack('J/a*', scalar(reverse($_))) }
   sort
   map {
      my ($l, $n) = /^([A-Z]+)([0-9]{1,9})\z/
         or die("Unexpected data");

      pack('a* a* J>', $l, length($l) == 1 ? "\x00" : "\xFF", $n) . reverse(pack('J/a*', $_))
   }
   @unsorted;

注意:除了检查之外,可以处理更大的数字而不更改任何内容,具体取决于您的Perl构建。

答案 1 :(得分:-2)

正如一些海报所写,问题并不完全清楚。尽管如此,我的解决方案尝试如下。我没有测试过这段代码,所以你可能需要稍微调整一下才能使它工作。

my @sorted_keys = sort by_letters_then_numbers keys %hash;

sub by_letters_then_numbers
{
    if ($a !~ /^([a-zA-Z]+)\s*(\d+)$/) {return 0};
    my $a_letters = $1;
    my $a_number = $2;
    if ($b !~ /^([a-zA-Z]+)\s*(\d+)$/) {return 0};
    my $b_letters = $1;
    my $b_number = $2;
    return(($a_letters cmp $b_letters) || ($a_number <=> $b_number));
}