如何使用正则表达式查找哈希中的键

时间:2013-12-05 18:52:40

标签: regex perl hash

我有6个十六进制哈希,需要计算其中有多少个键以AA00AB10开头,其中有多少键的键以两个字符串开头

对于每个哈希我都这样做了:

if (exists $hash{AA00}) {
    $AA00 +=1;
}
if (exists $hash{AB10}) {
    $AB10 += 1;
}
if (exists $hash{AA00} and exists $hash{AA10}) {
    $both += 1;
}

但是我只计算包含AA00AB10作为键的哈希值,但我还想计算包含的哈希值,比如说AA001。我可以使用正则表达式吗?

1 个答案:

答案 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::MoreUtilsanyanygrep非常相似,但只要找到匹配就会返回。要使用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)。在这种情况下,您不能简单地将两个小计加起来。例如,如果您希望键只在字符串中的任何位置包含AA00AB10,而不一定在开头,则需要执行以下操作:

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循环可以更有效地完成这项工作。