2

I was trying to follow some examples to use smart matching in the following piece of code, but failed (nothing was filtered out). How can I use smart matching here to match against multiple regexes at once?

my $regexes_to_filter_a = ("tmp", "temp", "del")
my @organism_dirs = (); # this will hold final list of dirs to processs

my @subdirs = File::Find::Rule->directory->maxdepth(1)->in($root_dir);
foreach my $subdir (@subdirs) {
    my $filter = 0;

    # IMPROVE: can do smart matching here
    foreach my $regex ( @{$regexes_to_filter_a} ) {
        if ( basename($subdir) =~ $regex ) {
            $filter = 1; # filter out this dir
            last;
        }
    }

    unless ($filter) {
        push @organism_dirs, $subdir;
    }
}
ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110
David B
  • 29,258
  • 50
  • 133
  • 186
  • possible duplicate of [How to match against multiple regexes in Perl?](http://stackoverflow.com/questions/3694322/how-to-match-against-multiple-regexes-in-perl) – brian d foy Sep 16 '10 at 07:47

2 Answers2

3

You don't need smart matching here. The ~~ with a single regex on the right hand side and a string on the left hand side might as well be a =~, just like you have it. What are you trying to do?

For your match, you have two ways to go. If you want to use a string as a pattern, you need to use the match operator:

 basename($subdir) =~ m/$regex/

If you want to not use the match operator, as you have it now, you need a regex object:

 my $regexes_to_filter_a = (qr/tmp/, qr/temp/, qr/del/);

I guess you could match all the regexes at once. Note that if you are going to set maxdepth to 1, you don't really need File::Find::Rule. If you aren't going to walk a directory structure, don't use a module designed to walk a directory structure:

my $regexes_to_filter_a = (qr/tmp/, qr/temp/, qr/del/);
my @organism_dirs = ();

foreach my $subdir ( glob( '*' ) ) {
    next unless -d $subdir;
    unless (basename($subdir) ~~ @regexes_to_filter_a) {
        push @organism_dirs, $subdir;
            } 
        }

I think all of that is too much work though. If you want to exclude known, static directory names (so, not patterns), just use a hash:

my %ignore = map { $_, 1 } qw( tmp temp del );

my @organism_dirs = 
    grep { ! exists $ignore{ basename($_) } } 
    glob( "$rootdir/*" );

If you really want to use the smart match:

my %ignore = map { $_, 1 } qw( tmp temp del );

my @organism_dirs = 
    grep { basename($_) ~~ %ignore } 
    glob( "$rootdir/*" );
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • I think he's trying to avoid the explicit loop and smartmatch against an array of regexs. – cjm Sep 16 '10 at 07:40
  • I just want to get the full paths of all subdirs under $root_dir, except those which match one of the regexes. – David B Sep 16 '10 at 08:39
2

Here is a quick untested change to your example:

my @regexes_to_filter_a = (qr/^tmp$/, qr/^temp/, qr/del/);
my @organism_dirs = (); # this will hold final list of dirs to processs

my @subdirs = File::Find::Rule->directory->maxdepth(1)->in($root_dir);
foreach my $subdir (@subdirs) {

    unless (basename($subdir) ~~ @regexes_to_filter_a) {
        push @organism_dirs, $subdir;
    }
}

The key changes are:

i) should be either @array = (...list...); or $array_ref = [...list...];

my @regexes_to_filter_a = ("tmp", "temp", "del");

ii) and change to using smart match. Below checks that basename($subdir) is in (~~) the @regexes_to_filter_a array. So no need to loop through the array and do individual regex checks.

unless (basename($subdir) ~~ @regexes_to_filter_a) { ... }

/I3az/

draegtun
  • 22,441
  • 5
  • 48
  • 71
  • You'll need to make those strings regex objects :) – brian d foy Sep 16 '10 at 07:44
  • I wonder if OP did really need a regex though? If so then `my @regexes_to_filter_a = (qr/tmp/, qr/temp/, qr/del/);` is required. – draegtun Sep 16 '10 at 07:58
  • +1 Thanks draegtun! Stupid me - I used `()` instead of `[]` for the array reference. – David B Sep 16 '10 at 08:45
  • 1
    Be aware that smart matching against a string is **NOT** the same as smart matching against a regex. Your example is actually doing `eq`, not a regex match. – cjm Sep 16 '10 at 09:16
  • @cjm: I know... see my comment reply to brian above :) I'll amend example to avoid more confusion. – draegtun Sep 16 '10 at 09:46