在Perl中,我如何使用自定义排序对哈希键进行排序?

5

我正在处理一个文件哈希值的任务,而且这个任务必须按照特定的顺序完成。大多数人会说可以按以下方式对列表进行排序:

for my $k (sort keys %my_hash)
{
    print "$k=>$my_hash{$k}, ";
}

但是,我需要按照非字母顺序排序,实际上键以一个单词开头,然后是下划线_,然后它们按照G数字L到任何一个M、P、R、T或D的顺序排序(例如word_G.txtword_2.txt、...、word_P.txt)。有没有办法按照自定义顺序排序?


只需在sort关键字后面放置您的排序条件:while my $k (sort { $a < $b unless $a eq '_' } keys %my_hash)等等... - rubber boots
3个回答

13

有没有一种方法可以按照自定义顺序排序?

是的。请参见sort

例如:

#!/usr/bin/env perl

use warnings; use strict;

my @order = qw(G 1 2 3 L M P R T D);

my %order_map = map { $order[$_] => $_ } 0 .. $#order;

my $pat = join '|', @order;

my @input = qw(word_P.txt word_2.txt word_G.txt);

my @sorted = sort {
    my ($x, $y) = map /^word_($pat)[.]txt\z/, $a, $b;
    $order_map{$x} <=> $order_map{$y}
} @input;

print "@sorted\n";

1
你并没有清楚地解释你的需求,所以我无法弄清楚什么可以解决特定的问题。要以非默认方式进行排序,需要编写一个比较函数。如果你解释一下这个函数需要做什么,我们可能能够帮忙。 - Sinan Ünür
我认为那是我能解释的最好方式,所以让我们换一种方式来表达。与其比较 A,B,C,...,X,Y,Z,我想比较的是 G,1,2,3,L,M,P,R,T,D - Eric Fossum
在左侧,您有26个字母,在右侧,您有10个字符,因此我不知道它们应该如何相互映射。但是,如果您只想将以“G”开头的单词映射到以“1”开头的单词之前进行排序,您应该能够调整我发布的示例以满足您的需求。 - Sinan Ünür
我以为那些是输出文件。 - Sinan Ünür
它们是已构建的文件,需要推送到另一个位置,但您编辑后的答案非常有帮助。非常感谢您的帮助,我做了一个修改。我将map更改为map /^word_($pat)\d?\.txt\z/, $a, $b;,因为有些文件实际上是word_P1、word_P2等;但这是我的错误,没有具体说明。 - Eric Fossum
显示剩余2条评论

4
use 5.014;

sub rank {
    my ($word) = @_;
    $word =~ s{\A \w+ _}{}msx;
    return do {
        given ($word) {
            0 when /\A G/msx;
            1 when /\A [0-9]/msx;
            2 when /\A L/msx;
            3 when /\A [MPRTD]/msx;
            default { 1000 };
        }
    };
}

say for sort { rank($a) <=> rank($b) } qw(word_P.txt word_2.txt word_G.txt);

输出:

word_G.txt
word_2.txt
word_P.txt

编辑:在Perl 5.14之前,请使用临时变量。

use 5.010;
⋮
return do {
    my $dummy;
    given ($word) {
        $dummy = 0 when /\A G/msx;
        $dummy = 1 when /\A [0-9]/msx;
        $dummy = 2 when /\A L/msx;
        $dummy = 3 when /\A [MPRTD]/msx;
        default { $dummy = 1000 };
    }
    $dummy;
};

我将你的解决方案复制粘贴到perl中,它没有报错,但是顺序P、2、G是提供的顺序... - Eric Fossum
根据http://p3rl.org/perl5140delta#given-return-values的要求,将最低所需版本提升。 - daxim

0

我有一个特定的使用情况,我想先按照某些值排序,然后将其他值放在最后,最后将所有其他值按字母顺序排列。

这是我的解决方案:

my @sorted = sort {
    my @order = qw(Mike Dave - Tom Joe);
    my ($x,$y) = (undef,undef);
    for (my $i = 0; $i <= $#order; $i++) {
        my $token = $order[$i];
        $x = $i if ($token eq $a or (not defined $x and $token eq "-"));
        $y = $i if ($token eq $b or (not defined $y and $token eq "-"));
    }
    $x <=> $y or
    $a cmp $b
} @ARGV;

输出:

$ perl customsort.pl Tom Z Mike A Joe X Dave G
Mike Dave A G X Z Tom Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接