【问题标题】:perl sort substrings using array to determine collating sequenceperl使用数组对子字符串进行排序以确定整理顺序
【发布时间】:2020-10-22 20:01:29
【问题描述】:

我有一个表单的字符串列表:

CLUB1_20201008_EVE
CLUB1_20201008_AFT
CLUB1_20201008_AM
CLUB1_20201008_AM2
CLUB1_20201008_PM
CLUB1_20201008_NIGHT
CLUB2_20201008_EVE
CLUB2_20201008_AFT
CLUB2_20201008_AM
CLUB2_20201008_AM2
CLUB2_20201008_PM
CLUB2_20201008_NIGHT

我可以按俱乐部名称和日期进行排序,但一天中的时间需要像 AM,AM2,AFT,PM,EVE,NIGHT 这样排序。这些显然是不可排序的,因为它们的顺序不是按字母顺序排列的。

如何使用辅助数组让它们按正确的顺序排序?

谢谢,米尔特

预期输出 CLUB1_20201008_NIGHT CLUB2_20201008_NIGHT CLUB1_20201008_EVE CLUB2_20201008_EVE CLUB1_20201008_PM CLUB2_20201008_PM CLUB1_20201008_AFT CLUB2_20201008_AFT CLUB1_20201008_AM2 CLUB2_20201008_AM2 CLUB1_20201008_AM CLUB2_20201008_AM

【问题讨论】:

  • 你能显示预期的输出吗?

标签: perl sorting collate


【解决方案1】:

诀窍是在每个时间字符串和可以排序的数字之间创建一个映射。

my @shifts = qw( AM AM2 AFT PM EVE NIGHT );
my %shift_order = map { $shifts[$_] => $_ } 0..$#shifts;

基于Sort-Key 的解决方案将是最干净和最快的。

use Sort::Key::Multi qw( ssukeysort );

my @shifts = qw( AM AM2 AFT PM EVE NIGHT );
my %shift_order = map { $shifts[$_] => $_ } 0..$#shifts;

my @sorted =
   ssukeysort {
      my ($club, $date, $shift) = split /_/;
      ( $club, $date, $shift_order{$shift} )
   }
      @unsorted;

没有模块的简单解决方案:

my @shifts = qw( AM AM2 AFT PM EVE NIGHT );
my %shift_order = map { $shifts[$_] => $_ } 0..$#shifts;

my @sorted =
   sort {
      my ($club_a, $date_a, $shift_a) = split /_/, $a;
      my ($club_b, $date_b, $shift_b) = split /_/, $b;

      $club_a cmp $club_b
         or
      $date_a cmp $date_b
         or
      $shift_order{$shift_a} <=> $shift_order{$shift_b}
   }
      @unsorted;

无模块的高性能解决方案:

my @shifts = qw( AM AM2 AFT PM EVE NIGHT );
my %shift_order = map { $shifts[$_] => $_ } 0..$#shifts;

my @sorted =
   map { ( split /\0/ )[-1] }
      sort
         map {
            my ($club, $date, $shift) = split /_/;
            join("\0", $club, $date, $shift_order{$shift}, $_)
         }
            @unsorted;

【讨论】:

    【解决方案2】:

    一种方法是使用哈希表将字符串映射到它们的数字顺序,然后根据它进行比较:

    #!/usr/bin/env perl
    use warnings;
    use strict;
    use feature qw/say state/;
    
    my @clubs = qw/CLUB1_20201008_EVE CLUB1_20201008_AFT CLUB1_20201008_AM
    CLUB1_20201008_AM2 CLUB1_20201008_PM CLUB1_20201008_NIGHT
    CLUB2_20201008_EVE CLUB2_20201008_AFT CLUB2_20201008_AM
    CLUB1_20201008_AM2 CLUB1_20201008_PM CLUB1_20201008_NIGHT/;
    
    sub sort_clubs {
        state $mappings = { AM => 1, AM2 => 2, AFT => 3, PM => 4,
                            EVE => 5, NIGHT => 6 };
    
        # For use with a Schwartzian Transform of the original data.
        # Expected format of arguments: [ [ club name, date, time ], original string ]
        my $cmp = $a->[0][0] cmp $b->[0][0];
        if ($cmp != 0) {
            return $cmp;
        }
        $cmp = $a->[0][1] <=> $b->[0][1];
        if ($cmp != 0) {
            return $cmp;
        } else {
            return $mappings->{$a->[0][2]} <=> $mappings->{$b->[0][2]}
        }
    }
    
    @clubs = map { $_->[1] } sort sort_clubs map { [ [ split(/_/, $_) ], $_ ] } @clubs;
    say "@clubs";
    

    如果您以前没有见过用于排序的成语,维基百科上有关于 Schwartzian Transform 的信息。

    【讨论】:

    • ST?呸。 GRT! :) 不过,说真的,ST 和 GRT 都可能是矫枉过正,在这里增加了不必要的复杂性。 (my answer 中有一个基于 GRT 的解决方案,但不是我推荐的。)
    【解决方案3】:

    有许多可能的方法来达到预期的结果。

    注意:代码假定基于您没有尝试一次对俱乐部、日期小时分钟和一天时间进行排序,因为它没有在您的问题中声明。所需的排序输出样本将有助于解决您的问题——我们无法理解您的想法。

    请研究以下两种可能的方法。

    您根据字符串中的 time day 索引声明了排序顺序——让我们以某种方式使用它。为简单起见,我将使用AM,AM2,AFT,PM,EVE,NIGHT 作为初始化字符串。

    第一种方法使用%order 散列,time day 是表示数字顺序的键和数字。以数字顺序作为键存储在 HoA 中的字符串。填充哈希后,只需根据数字键顺序打印,并保留它们在输入时出现的顺序。

    use strict;
    use warnings;
    use feature 'say';
    
    my $count = 0;
    my @set   = split ',', 'AM,AM2,AFT,PM,EVE,NIGHT';
    my %order = map { $_ => $count++ } @set;
    my %result;
    
    while( <DATA> ) {
        chomp;
        for my $k ( keys %order ) {
            push @{$result{$order{$k}}}, $_ if /_$k\z/;
        }
    }
    
    for( sort {$a <=> $b} keys %result ) {
        say for @{ $result{$_} };
    }
    
    
    __DATA__
    CLUB1_20201008_EVE
    CLUB1_20201008_AFT
    CLUB1_20201008_AM
    CLUB1_20201008_AM2
    CLUB1_20201008_PM
    CLUB1_20201008_NIGHT
    CLUB2_20201008_EVE
    CLUB2_20201008_AFT
    CLUB2_20201008_AM
    CLUB1_20201008_AM2
    CLUB1_20201008_PM
    CLUB1_20201008_NIGHT
    

    输出

    CLUB1_20201008_AM
    CLUB2_20201008_AM
    CLUB1_20201008_AM2
    CLUB1_20201008_AM2
    CLUB1_20201008_AFT
    CLUB2_20201008_AFT
    CLUB1_20201008_PM
    CLUB1_20201008_PM
    CLUB1_20201008_EVE
    CLUB2_20201008_EVE
    CLUB1_20201008_NIGHT
    CLUB1_20201008_NIGHT
    

    第二种方法更简单。根据 time day 索引(在行尾)推送 HoA %result 中的行。然后根据预定义的$order数组打印打印HoA。

    use strict;
    use warnings;
    use feature 'say';
    
    my @order = split ',', 'AM,AM2,AFT,PM,EVE,NIGHT';
    my %result;
    
    while( <DATA> ) {
        chomp;
        push @{$result{$1}}, $_ if /_([^_]+)\z/;
    }
    
    for( @order ) {
        say for @{ $result{$_} };
    }
    
    
    __DATA__
    CLUB1_20201008_EVE
    CLUB1_20201008_AFT
    CLUB1_20201008_AM
    CLUB1_20201008_AM2
    CLUB1_20201008_PM
    CLUB1_20201008_NIGHT
    CLUB2_20201008_EVE
    CLUB2_20201008_AFT
    CLUB2_20201008_AM
    CLUB1_20201008_AM2
    CLUB1_20201008_PM
    CLUB1_20201008_NIGHT
    

    输出

    CLUB1_20201008_AM
    CLUB2_20201008_AM
    CLUB1_20201008_AM2
    CLUB1_20201008_AM2
    CLUB1_20201008_AFT
    CLUB2_20201008_AFT
    CLUB1_20201008_PM
    CLUB1_20201008_PM
    CLUB1_20201008_EVE
    CLUB2_20201008_EVE
    CLUB1_20201008_NIGHT
    CLUB1_20201008_NIGHT
    

    【讨论】:

      猜你喜欢
      • 2012-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多