我有6个十六进制哈希,需要计算其中有多少个键以AA00
,AB10
开头,其中有多少键的键以两个字符串开头
对于每个哈希我都这样做了:
if (exists $hash{AA00}) {
$AA00 +=1;
}
if (exists $hash{AB10}) {
$AB10 += 1;
}
if (exists $hash{AA00} and exists $hash{AA10}) {
$both += 1;
}
但是我只计算包含AA00
或AB10
作为键的哈希值,但我还想计算包含的哈希值,比如说AA001
。我可以使用正则表达式吗?
答案 0 :(得分:4)
我完全误解了你的问题。要查找与正则表达式匹配的哈希的数量(与单哈希中匹配正则表达式的键的数量相对),仍然可以使用我在之前的回答中概述的grep
方法。但是,这次你需要循环哈希(我假设你将它们存储在一个数组中,如果你有600万个)并且每个运行grep
两次:
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my @array = (
{ AA00 => 'foo' },
{ AB10 => 'bar' },
{ AA001 => 'foo' },
{ AA00 => 'foo', AB10 => 'bar' }
);
my ($hashes_with_aa00, $hashes_with_ab10, $hashes_with_both) = (0, 0, 0);
foreach my $hash (@array) {
my $aa_count = grep { /^AA00/ } keys %$hash;
my $ab_count = grep { /^AB10/ } keys %$hash;
$hashes_with_aa00++ if $aa_count;
$hashes_with_ab10++ if $ab_count;
$hashes_with_both++ if $aa_count and $ab_count;
}
say "AA00: $hashes_with_aa00";
say "AB10: $hashes_with_ab10";
say "Both: $hashes_with_both";
AA00: 3
AB10: 2
Both: 1
这很有效,但在性能方面相当差:grep
遍历每个哈希的键列表中的每个元素,我们称之为两次每个哈希!
由于我们不关心每个哈希中有多少个键匹配,只有是否匹配,更好的解决方案是来自List::MoreUtils的any
。 any
与grep
非常相似,但只要找到匹配就会返回。要使用any
代替grep
,请更改此项:
foreach my $hash (@array) {
my $aa_count = grep { /^AA00/ } keys %$hash;
my $ab_count = grep { /^AB10/ } keys %$hash;
$hashes_with_aa00++ if $aa_count;
$hashes_with_ab10++ if $ab_count;
$hashes_with_both++ if $aa_count and $ab_count;
}
到此:
use List::MoreUtils 'any';
foreach my $hash (@array) {
my $aa_exists = any { /^AA00/ } keys %$hash;
my $ab_exists = any { /^AB10/ } keys %$hash;
$hashes_with_aa00++ if $aa_exists;
$hashes_with_ab10++ if $ab_exists;
$hashes_with_both++ if $aa_exists and $ab_exists;
}
请注意,我更改了变量名称以更好地反映其含义。
这在性能方面要好得多,但正如Borodin在你的问题评论中指出的那样,你通过不使用特定键访问它们而失去了哈希的速度优势。您可能希望相应地更改数据结构。
这是我原来的答案,基于对您的问题的误解。我将其放弃,因为我认为它对类似情况有用。
要计算与单个哈希中的正则表达式匹配的键数,可以使用grep
:
my $aa_count = grep { /^AA00/ } keys %hash;
my $ab_count = grep { /^AB10/ } keys %hash;
my $both = $aa_count + $ab_count;
正如HunterMcMillen在评论中指出的那样,没有必要再次搜索哈希键来获得总计数;在这种情况下,您可以简单地添加两个小计。你可以逃脱这个因为你正在寻找的两种模式是相互排斥的;换句话说,你不能拥有一个以AA00
和 AB10
开头的密钥。
在更一般的情况下,单个键可能匹配两种模式(感谢Borodin)。在这种情况下,您不能简单地将两个小计加起来。例如,如果您希望键只在字符串中的任何位置包含AA00
或AB10
,而不一定在开头,则需要执行以下操作:
my $aa_count = grep { /AA00/ } keys %hash;
my $ab_count = grep { /AB10/ } keys %hash;
my $both = grep { /(?:AA00|AB10)/ } keys %hash;
请注意,这会多次调用grep
,这意味着多次遍历整个哈希。使用像FlyingFrog和Kenosis那样的单for
循环可以更有效地完成这项工作。