facedag has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I have an array filled with identification numbers, and an array filled with filenames that are based on those numbers. I have about 8000 files, and about 200 of them must get deleted, and I was trying to write a Perl script to do this.

I have the ID numbers in @ID, and the filenames in @files. I attempted to write a foreach loop that checks each ID against the files, and prints the file that matches the ID. Here is what I have:

foreach $ID(@ID) { chomp $ID; if ($ID=~ m/@files/) { print @files } }

Not only does this not do what I would like, it doesn't have any output at all. Would someone please point me in the right direction?

Thanks for reading,
-Jonathan

Replies are listed 'Best First'.
Re: Quick Question?
by jrsimmon (Hermit) on Jul 21, 2009 at 02:55 UTC
    Not tested, but fairly straightforward. This assumes that the exact string represented by $ID shows up somewhere within the filename that you want to match.
    use strict; use warnings; chomp(@ID); foreach my $ID(@ID) { foreach my $file (@files){ if ($ID=~ /$file/) { print "$file\n"; #if the matches are unique, you can bail this foreach after a ma +tch last; } } }
Re: Quick Question?
by toolic (Bishop) on Jul 21, 2009 at 03:03 UTC
    quick answer... one way to do it is to use 2 for loops:
    use warnings; use strict; my @ids = 0..5; my @files = 3..8; for my $id (@ids) { for my $file (@files) { print "$file\n" if $id eq $file; } } __END__ 3 4 5

    Feel free to change the title of your node to something more useful like: "Check if 2 arrays match".

Re: Quick Question?
by halfcountplus (Hermit) on Jul 21, 2009 at 03:02 UTC
    Maybe you want something more like this
    #!/usr/bin/perl -w use strict; my @ray = qw(one two three); my @two = qw(two next four); foreach my $x (@ray) { foreach my $y (@two) { print $x if ($x eq $y); } }
    If you just use @two in the regex match, it will expand to the entire array, ie, you are asking if the filename matches the entire list of filenames, not one of them. Eg.
    if ($ID=~ m/one.txt two.txt three.txt/)
    which no file will have that name. In this situation methinks it matters not so much, but "eq" will only match exactly, whereas =~ will match if the pattern is a substring.
Re: Check If 2 Arrays Match
by bichonfrise74 (Vicar) on Jul 21, 2009 at 03:50 UTC
    Another possible solution?
    #!/usr/bin/perl use strict; my @ids = 0..5; my @files = 3..8; my %record; $record{$_}++ for (@ids, @files); print map { "Same file and id: $_\n" if $record{$_} > 1 } %record;
Re: Check If 2 Arrays Match
by johngg (Canon) on Jul 21, 2009 at 11:23 UTC
    ... with filenames that are based on those numbers.

    If the derivation of filename from ID is predictable, it might be more efficient to create a hash of filenames from the IDs then use grep to select appropriate files from your @files array rather than keep looping over files and IDs.

    use strict; use warnings; my @IDs = qw{ pj7023 ge4872 dr90324 jc824 }; my @files = qw{ XYZ_dr90324.txt XYZ_at728.txt XYZ_jc824.txt XYZ_uf72082.txt XYZ_tc43.txt XYZ_pj7023.txt XYZ_yj82195.txt XYZ_fw607.txt }; my %IDnames = map { my $key = q{XYZ_} . $_ . q{.txt}; $key => 1 } @IDs; my @selectedFiles = grep exists $IDnames{ $_ }, @files; print qq{$_\n} for @selectedFiles;

    The output.

    XYZ_dr90324.txt XYZ_jc824.txt XYZ_pj7023.txt

    It might be that the filename can't be confidently derived from the ID and this method will not work but I thought I'd post the idea just in case.

    Cheers,

    JohnGG

Re: Check If 2 Arrays Match
by facedag (Initiate) on Jul 21, 2009 at 04:02 UTC
    Thank you for all of the responses, this did the trick:
    chomp (@ID); foreach $ID(@ID){ foreach $file(@files){ print "$file" if ($file =~ /($ID)/); } }

      Unless your ids are always exactly the same length, you might want to replace /($ID)/ with /^$ID$/. Otherwise "123" will match all of the following files: "123", "1234", and "4123".

      I also omitted the parentheses around $ID. "(...)" makes Perl do extra work to "capture" whatever is between the parenthesis. Since you already have the value of $ID you don't need to capture it.

      Another potential bug. If your id contains fullstops, i.e. '.' or other special regular expresssion characters you should probably escape the id, i.e. /^\Q$ID\E$/. The reason is that Perl will read the dot as a wildcard, so "1.2" will match "1.2", "122", "152", and so on.

      Best, beth

      One more:
      foreach my $id(@ID){ print "$id\n" if grep(/^$id$/, @files); }

      «A contentious debate is always associated with a lack of valid arguments.»