Steve
Steve

Reputation: 11

Find matching list item where another list has the match strings

I have a list of text strings and a hash of RE / match strings + tags and I need, unsurprisingly, to match them up. I also need to know if there is any item in the first list that isn't matched by one of the items in the second, for example:

@strings = qw(red orange yellow blue);
%matches = (
    re => "apple",
    or => "orange",
    ye => "banana",
);

I need to match up each of the $strings with a $hash_value (red <-> apple for example) and raise an error for 'blue'.

I can do this with two nested loops, iterating through @strings with the $hash_keys looking for a match and getting the hash value. I think I need to use a flag variable to identify whether the inner $hash_keys loop finished without matching anything.

I can probably also do it by iterating through the $hash_keys and 'grep'ing @strings, but I don't know how to spot unmatched $strings.

Both of those options seems really clunky and I feel like there must be a 'cleaner' way. Am I missing something obvious?

EDIT

Apologies, the code I have is something like:

foreach $string (@strings) {
  $match = "false";
  foreach $key (keys %matches) {
    if ( /$key/ =~ $string) {
      &do_my_thing($string,$matches{$key});
      $match = "true";
      break;
      }
    }
  ( $match eq "false" ) && raise_unmatched_error($string);
  }

This does what I want, identifies which mask (key) matched the string and also flags unmatched strings, but it just looks inelegant. It seems to me that it should be possible to use map, maybe with grep, to a) match the hash keys with the strings, and b) identify unmatched strings.

I might be over thinking it though; it works, it's probably maintainable, maybe it doesn't need to be anything else.

Upvotes: 1

Views: 86

Answers (1)

Dave Cross
Dave Cross

Reputation: 69244

Take all of the keys in your hash and turn them into a regex (using the alternation operator so that you can match on any of them). You can then do one regex match against each string in @strings. If you put capturing parentheses around the regex, the string that matches will end up in $1. And if the string doesn't match the regex (as with "blue") you can display the required warning.

#!/usr/bin/perl

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

my @strings = qw[red orange yellow blue];
my %matches = (
  re => 'apple',
  or => 'orange',
  ye => 'banana',
);

my $match_re = join '|', keys %matches;
say "DEBUG: $match_re";

for (@strings) {
  if (/($match_re)/) {
    say "$_ -> $matches{$1}";
  } else {
    say "No match for '$_'";
  }
}

Upvotes: 3

Related Questions