【问题标题】:Selectively counting delimited field values and creating a hash using map选择性地计算分隔字段值并使用映射创建哈希
【发布时间】:2010-12-10 00:50:59
【问题描述】:

我有一个以竖线分隔的文本文件,其中包含日期和数字,指示程序中其他地方的行顺序。我希望做的是从该文件中创建一个哈希,使用年份作为键,值是那一年的最大序列(我基本上需要每年实现一个自动递增的键),例如来自

2000|1
2003|9
2000|5
2000|21
2003|4

我会以这样的哈希结束:

%hash = {
    2000 => 21,
    2003 => 9
}

我已经设法将文件分成年份和序列部分(我认为不是很好),如下所示:

my @dates = map {
    my @temp = split /\|/;
    join "|", (split /\//, $temp[1])[-1], $temp[4] || 0; #0 because some records
                                                         #mightn't have a sequence
} @info

我可以做一些简洁的事情来使用该数据创建哈希吗?

谢谢

【问题讨论】:

    标签: perl data-structures hash map split


    【解决方案1】:

    如果我理解你的话,你就快到了。您需要做的就是从 map 中返回键和值并按顺序排序,而不是加入它们。

    my %hash = 
        map @$_,
        sort { $a->[1] <=> $b->[1] }
        map {
            my @temp = split /\|/;
            my $date = (split /\//, $temp[1])[-1];
            my $seq = $temp[4] || 0; #0 because some records mightn't have a sequence
            [ $date, $seq ]
        } @info;
    

    但仅在当前序列中使用 for 进行迭代并设置哈希 高于该日期之前的最大值可能是一个更好的主意。

    小心那些 {};你说的地方

    %hash = {
        2000 => 21,
        2003 => 9
    }
    

    您的意思是 () 代替(或分配给引用 $hash),因为那里的 {} 创建了一个匿名哈希并返回对它的引用。

    【讨论】:

    • 您的map sort map 很聪明,可以胜任。如您所知,这种方法比whilefor 循环的计算成本更高——“排序”并不便宜。 ++ 用于解决所提出的问题,++ 用于演示链式 map 解决方案,++ 用于提及这是解决问题的坏方法,但是 - 没有解释为什么这种方法是一个坏主意。
    【解决方案2】:

    你可以这样写..不太清楚你为什么想要/需要使用地图(请解释)

    #!/usr/bin/perl -w
    use strict;
    use warnings;
    
    my %hash;
    
    while(<DATA>) {
       chomp();
       my ($year,$sequence)=split('\|');
    
        $sequence = 0 unless (defined ($sequence));       
    
        next if (exists $hash{$year} and $sequence < $hash{$year});
    
       $hash{$year}=$sequence;
    }
    
    
    __DATA__
    2000|1
    2003|9
    2000|5
    2000|21
    2003|4
    

    我添加了 $sequence = 0 unless defined ($sequence);因为你的 sn-p 中的评论。我相信我可能会理解您的意图..(输入格式有效/一致,或者不是..)

    【讨论】:

      【解决方案3】:

      map 对列表中的每个项目进行操作,并构建要传递的结果列表。所以,你不能真正做你想要的那种检查(保持最大序列值),除非你构建一个临时哈希,最终包含你试图构建的数据作为`map的返回值.

      my %results = map {
      
          my( $y, $s ) = split '[|]', $_;
      
          seq_is_gt_year_seq( $y, $s ) 
             ? ( $y, $s )
             : ();
      } @year_pipe_seq;
      

      为了实现seq_is_gt_year_seq(),我们最终不得不构建一个临时哈希来存储每年及其最大序列值以供查找。

      您应该使用增量构建查找的方法,例如 forwhile 循环。

      【讨论】:

        【解决方案4】:

        map { BLOCK } LIST always 通常(除非 BLOCK 有时计算结果为空列表)返回的列表至少与 LIST 一样大,并且可能不会如果您确实想简单地用最新数据覆盖重复的键,则可以这样做。比如:

            my %hash;
            for (@info) {
                my @temp = split /\|/;
                my $key = (split /\//, $temp[1]);
                my $value = $temp[4] || 0;
                $hash{$key} = $value unless defined $hash{$key} && $hash{$key}>=$value;
            }
        

        会起作用。最后一行有条件地更新了哈希表,这是你在map语句中不能做(或者至少不能很方便地做)的事情。

        【讨论】:

        • 映射块列表和映射表达式列表都返回列表中每个元素 0 个或多个元素:map { $_ == 5 ? () : qw(a b c) } 1..5 给出 a, b, c 作为其结果。如果您不想从列表中的元素中获取任何内容,则返回一个空列表。
        • @daotoad:我有一种感觉,有人会证明我错了。
        【解决方案5】:

        如果您有机会在读取文件时执行此处理,那么我会这样做。像这样的:

        my %year_count;
        while (my $line = <$fh>){
            chomp $line;
            my ($year, $num) = split /\|/, $line;
            if ($num > $year_count{$year} || !defined $year_count{$year})
                $year_count{$year} = $num;
            }
        }
        

        如果你想使用一个数组, map 并不是最好的选择(因为你没有转换列表,你正在处理它到不同的东西)。老实说,最明智的数组处理可能与上述相同,但在 foreach 中:

        my %year_count;
        foreach my $line (@info){
            my ($year, $num) = split /\|/, $line;
            if ($num > $year_count{$year} || !defined $year_count{$year})
                $year_count{$year} = $num;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-01-15
          • 2023-04-08
          • 1970-01-01
          • 2022-08-23
          • 1970-01-01
          • 2018-02-03
          • 2021-06-11
          相关资源
          最近更新 更多