【问题标题】:How to get first value less than threshold value from hash in Perl如何从Perl中的哈希中获取小于阈值的第一个值
【发布时间】:2020-12-01 02:30:41
【问题描述】:

我有一个哈希值,其中包含 unixtime 作为键和卷作为值。

我需要从体积小于threshold 值(我在开头定义的阈值)的哈希中获取 unixtime 和体积对,并且该对出现在哈希的第一位。

下面是我的脚本:

use strict;
use warnings;

use Data::Dumper;

use List::Util qw(reduce);
use POSIX qw( strftime );

my $threshold = 20;

my %hash = (
          '1596561300' => '19',
          '1596561306' => '12',
          '1596561312' => '17',
          '1596561318' => '20',
          '1596561324' => '23',
          '1596561330' => '11',
          '1596561336' => '16',
          '1596561342' => '15',
          '1596561348' => '13',
          '1596561354' => '17'
);

my $key = reduce { $hash{$a} <= $hash{$b} ? $a : $b } keys %hash;
my $val = $hash{$key};

$key = strftime("%Y-%m-%d %H:%M:%S", localtime($key));

print "Key=>$key :: Value=>$val\n";

在上面的脚本中,我能够从散列的所有值(卷)中获得最小散列值的 unixtime 卷。即,

Key=>2020-08-04 18:15:30 :: Value=>11

但我需要获取小于第一个/最小哈希键中出现的阈值的值。

对于上面的例子,它应该获取 ('1596561300' => '19') 即:

Key=>2020-08-04 18:15:00 :: Value=>19

我怎样才能得到它? TIA。

【问题讨论】:

  • 您能否告诉您是否有两个条目(例如 17 值)'1596561300' =&gt; '19', 等于 '2020-08-04 22:45:00' =&gt; 19 ,例如 '1596561354' =&gt; '19' 等于 '2020-08-04 22:45:54' =&gt; 19 在这种情况下您是哪个值会考虑吗?
  • @amitbhosale :我应该考虑首先发生的那个。即'2020-08-04 22:45:00 =&gt; 19'.

标签: perl hash


【解决方案1】:

您可以使用https://metacpan.org/pod/List::Util#pairmap提供的pairmap和unpairs方法

pairmap :类似于 perl 的 map 关键字,但将给定列表解释为偶数大小的对列表。它在列表上下文中多次调用 BLOCK,将 $a 和 $b 设置为来自 @kvlist 的连续值对。返回列表上下文中 BLOCK 返回的所有值的串联,或计数的数量在标量上下文中返回的项目。

unpairs : 对的反函数;此函数接受一个包含两个元素的 ARRAY 引用列表,并返回每个对中的两个值的扁平列表,

use strict;
use warnings;
use List::Util qw(pairmap unpairs min);
use POSIX qw( strftime );
use Data::Dumper qw(Dumper);

my $threshold = 20;

my %hash = (
          '1596561300' => '19',
          '1596561200' => '12',
          '1596561312' => '17',
          '1596561318' => '20',
          '1596561324' => '23',
          '1596561330' => '11',
          '1596561336' => '16',
          '1596561342' => '15',
          '1596561348' => '13',
          '1596561354' => '17'
);


# my @list = pairmap { BLOCK } @kvlist;
# parimap invokes the BLOCK multiple times => checking value equal to threshold-1 
# and retrun list 
# my @kvlist = unpairs @pairs
# unpair method takes a list of ARRAY references containing two elements each, 
# and returns a flattened list of the two values from each of the pairs,
my %h = unpairs (pairmap { ($b == ($threshold-1)) ? [$a ,$b] : () } %hash);

# if hash is having mutiple same values then get min key from hash
if (%h) {
    my $min_key = min(keys(%h));
    print "\n Key : ", strftime("%Y-%m-%d %H:%M:%S", localtime($min_key)) ," and Value :", $h{$min_key} , "\n";
} else {
    print "\n Not found data \n";
}

输出

 Key : 2020-08-04 22:45:00 and Value :19

【讨论】:

    【解决方案2】:

    您可以通过检查该值是否小于您的阈值并且该键是否小于您上次捕获的键来通过哈希(无需排序)。

    我只是在没有语法检查或编译器的情况下将其丢弃:

    my $threshold = 20;
    my $last;
    for my $key (keys %hash) {
      if ($hash{$key} <= $threshold && $key < ($last //= $key)) {
        $last = $key;
      }
    }
    
    if ($last) {
      printf "Key=>%s :: Value=>%s\n",
          strftime("%Y-%m-%d %H:%M:%S",
          localtime($last)),$hash{$last};
    }
    

    【讨论】:

      【解决方案3】:

      基本上,您需要做的就是通过哈希,检查值是否低于阈值,并记住符合该标准的最早时间戳:

      #!/usr/bin/env perl    
        
      use strict;
      use warnings;
      
      use POSIX qw( strftime );
      
      my $threshold = 20;
      my %hash = (
                '1596561300' => '19',
                '1596561306' => '12',
                '1596561312' => '17',
                '1596561318' => '20',
                '1596561324' => '23',
                '1596561330' => '11',
                '1596561336' => '16',
                '1596561342' => '15',
                '1596561348' => '13',
                '1596561354' => '17'
      );
      
      my $earliest;
      
      for (keys %hash) {
        # Ignore any entries with a volume above the threshold
        next if $hash{$_} >= $threshold;
      
        $earliest //= $_;  # Initialize it if it doesn't have a value yet
      
        $earliest = $_ if $_ < $earliest;
      }
      
      die "No volumes under threshold" unless defined $earliest;
      
      my $formatted = strftime("%Y-%m-%d %H:%M:%S", localtime($earliest));
      print "Key=>$formatted :: Value=>$hash{$earliest}\n";
      

      【讨论】:

        【解决方案4】:

        您可以反转哈希以翻转键和值,然后从阈值倒数直到找到存在的值。

        my %reversed = reverse %hash;
        my $needle   = $threshold - 1;
        
        $needle-- until exists $reversed{$needle};
        
        my $key = strftime("%Y-%m-%d %H:%M:%S", localtime($reversed{$needle}));
        my $val = $needle;
        
        print "Key=>$key :: Value=>$val\n";
        

        我认为我以前从未使用过until,但这似乎比while not exists 读起来更好。

        阈值为20的输出是

        Key=>2020-08-04 18:15:00 :: Value=>19
        

        【讨论】:

        • 请注意,只有在所有散列值都是唯一的情况下,反转散列才是可靠的。如果有重复项,则将它们转换为键(使用reverse)会导致键冲突,其中一个会覆盖其他键。在问题的示例数据中,有两个值为 17 的条目,因此其中一个时间戳丢失了。
        • @DaveSherohman 是的,我没看到。然而,接下来的问题是哪一个 OP 想要。您的解决方案具有相同的碰撞问题,因为您没有进行排序。所以你得到随机的(取决于 Perl 版本)密钥顺序。由于OP没有说他们想要最早的,我认为你也在做一个假设,尽管你的比我的要好得多。但我认为这个问题还不清楚。
        • 问题状态 “我需要获取小于第一个/最小哈希键中出现的阈值的值。”,我读到他想要最早的合格时间戳。如果您认为我的回答有问题,请在此处发表评论,因为该讨论与您的回答无关。
        【解决方案5】:

        我认为您无法避免按键对哈希进行排序:

        foreach my $k (sort keys %hash) {
                if( $hash{$k} <= $threshold ) {
                        print "Key=>", strftime("%Y-%m-%d %H:%M:%S", localtime($k)),
                                ":: Value=>", $hash{$k}, "\n";
                        last;
                }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-11-24
          • 2016-07-22
          • 1970-01-01
          • 1970-01-01
          • 2010-09-08
          • 2017-02-16
          • 1970-01-01
          • 2021-12-30
          • 2012-10-04
          相关资源
          最近更新 更多