Victor
Victor

Reputation: 29

How to check if a hash key is present in the hash by matching any letter/number for a character {say X}

I have a hash, say "Identifier" with keys such as FACD,BDCD,DDSE,CDSD. I would like to search if the key "FXXD" is present in the hash "Identifier" by considering X to match any alphanumeric. In this scenario, it should come back with found as "FXXD" matches "FACD" considering X can be anything.

Can hash key search be done by adding a character to match anything?

Any idea would be really helpful?

Thanks in Advance!

Upvotes: 2

Views: 725

Answers (4)

Dave Cross
Dave Cross

Reputation: 69284

If you're not too concerned with performance, you could use Tie::Hash::Regex.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Tie::Hash::Regex;

my %hash : Regex;

%hash = (
  FACD => 1,
  BDCD => 1,
  DDSE => 1,
  CDSD => 1,
);

if (exists $hash{'F\w\wD'}) {
  say 'Found key matching FXXD';
} else {
  say 'No key matching FXXD';
}

[Full disclosure - I wrote this module as a stupid demonstration of tying. I don't really recommend that you use it in production.]

Upvotes: 1

Shawn
Shawn

Reputation: 52439

First, turn the X's in your string into a regular expression character class that matches a single alphanumeric character, and then the good old grep/keys:

#!/usr/bin/perl
use warnings;
use strict;
use feature qw/say/;

my $test = { FACD => 1, BDCD => 1, DDSE => 1, CDSD => 1, QFADD => 1 };

sub matches {
  my ($hash, $key) = @_;

  # X matches all alphanumeric characters
  $key =~ s/X/[[:alnum:]]/g;
  my $pat = qr/^$key$/;
  return grep { m/$pat/ } keys %$hash;
}

for (qw/FXXD FXDD BXXX/) {
  say $_, (matches($test, $_) ? " matches!" : " doesn't match!");
}

Upvotes: 5

zdim
zdim

Reputation: 66891

To select keys that satisfy a pattern

my @spec_keys = grep { /F..D/ } keys %identifier;

where the F..D pattern can be anywhere in the key. If it should match the key without any leading or trailing characters then anchor it, /^F..D$/.

Upvotes: 5

Stefan Becker
Stefan Becker

Reputation: 5962

No you can't. A single hash is the wrong approach for this kind of search.

If your keys are fixed sized, e.g. 4 characters long as per your example, you could use multiple hashes. Here is a sketch of the algorithm:

my %first = (
   A => { AAAA => 1, ... all keys starting with A... },
   ...
);
my %second = {
   A => { AAAA => 1, ... all keys having A as 2nd character... },
   ...
);
my %third  = ...
my %fourth = ...

# match first (F) and last (D) character in key
my $matches_first = $first{F};
my $matches_last  = $fourth{D};
my @matches =
    grep { exists $matches_fourth->{$_} }
    keys %{ $matches_first };

In a real program you would generate the contents of %first etc from the list of keys and calculate the match code from the search pattern.

foreach my $c ('A'..'Z') {
    $first{$c}  = {};
    $second{$c} = {};
    $third{$c}  = {};
    $fourth{$c} = {}; 
}
foreach my $key (keys %identifier) {
    my($c1, $c2, $c3, $c4) = split(//, $key);
    $first{$c1}->{$key}++;
    $second{$c2}->{$key}++;
    $third{$c3}->{$key}++;
    $fourth($c4}->{$key}++;
}

EDIT 2: Depending on the amount of your data it might also be possible to use a simple search function, e.g. for your example

my @matches = grep { /^F..D$/ } keys %identifier; 

Upvotes: 1

Related Questions