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

Ola,

I have the following data
From DB AB1/1 AB1/5 AB2/5 From Input AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 AB2/13
What I need to do is search the DB array for matching values from the Input array. So to produce an output similar to this:
AB1/1 : Found AB1/1 AB1/2 : NotFound AB1/2 AB1/5 : Found AB1/5 AB1/6 : NotFound AB1/6 AB1/9 : NotFound AB1/9 AB2/2 : NotFound AB2/2 AB2/5 : Found Ab2/5 AB2/6 : NotFound Ab2/6 AB2/9 : NotFound AB2/9 AB2/13 : NotFound AB2/13
So I wrote this simple script:
#! c:/perl/bin/perl.exe # use strict; use warnings 'all'; my @NotFound; my @DB = qw(AB1/1 AB1/5 AB2/5); print "\nFrom DB\n"; for my $DBData (@DB) { print "$DBData\n"; } print "\nFrom Input\n"; my @Input = qw(AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 A +B2/13); for my $InputData (@Input) { print "$InputData\n"; } ################################################## print "\nSearching\n"; for my $DBData (@DB) { for my $InputData (@Input) { if ("$DBData" eq "$InputData") { print "$InputData : Found $DBData\n"; last; } else { print "$InputData : NotFound $DBData\n"; } } } print "\nSize of NotFound Array $#NotFound\n";
Which produced this incorrect output!
From DB AB1/1 AB1/5 AB2/5 From Input AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 AB2/13 Searching AB1/1 : Found AB1/1 AB1/1 : NotFound AB1/5 AB1/2 : NotFound AB1/5 AB1/5 : Found AB1/5 AB1/1 : NotFound AB2/5 AB1/2 : NotFound AB2/5 AB1/5 : NotFound AB2/5 AB1/6 : NotFound AB2/5 AB1/9 : NotFound AB2/5 AB2/2 : NotFound AB2/5 AB2/5 : Found AB2/5 Size of NotFound Array -1
I have spent sometime thinking about a solution for it, but the best I can produce is the above output.

Since coding problems like these are an absolute doddle for you guys, I wonder if someone can help me and show me how can I produce the required results efficiently and with minimal code?

Thanks for your help

Replies are listed 'Best First'.
Re: searching 2 arrays
by Zaxo (Archbishop) on Oct 19, 2005 at 12:06 UTC

    You can speed that up by making a hash of the reference array and testing for existence of keys.

    my %search; @search{@DB} = (); for (@Input) { print exists $search{$_} ? "$_ : Found $_\n" : "$_ : Not Found $_\n"; }

    Does your @DB come from a relational database? It may be a better design to let the rdbm do the work by constructing a query on your inputs.

    Update: The ?: operator is called the trinary op in perlop. It is like if..else.. but it returns a value, which is what's getting printed here. Syntax is condition ? if-value : else-value. A cool but little-used property of trinary is that it's an lvalue:

    $invert ? ($true, $false) : ($false, $true) = (0, 1);
    I think it's little used because there's not much call for it in well-designed code. Trinary is a borrowing from C.

    After Compline,
    Zaxo

      The ?: operator is called the trinary op in perlop.

      Actually—in case anyone searches and can't find it—perlop refers to it as the "ternary" operator. That's probably the more common terminology though "trinary" is a perfectly acceptable alternative. Another alternative is the "conditional" operator. And, sometimes it's called the "tertiary" operator but that's really just incorrect usage.

      -sauoq
      "My two cents aren't worth a dime.";
      
      No @DB doesn't come from RDM, I am constructing it.

      Thanks for your help

      But I don't understand/learnt how does the ? : work!

Re: searching 2 arrays
by virtualsue (Vicar) on Oct 19, 2005 at 12:09 UTC
Re: searching 2 arrays
by pg (Canon) on Oct 19, 2005 at 12:39 UTC

    That "last" made you miss entries. To make you realize your mistake, here is the code you can compare with: (But this is not the solution to go for speed. Say the two arrays are of size n and m. In the worst case, this solution costs n*m, whereas use a hash (for DB) will only cost the size of the input data)

    use strict; use warnings 'all'; my @DB = qw(AB1/1 AB1/5 AB2/5); my @Input = qw(AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 A +B2/13); for my $InputData (@Input) { my $found; for my $DBData (@DB) { if ($DBData eq $InputData) { $found = 1; last; } } print "$InputData : " . ($found?" ":" Not") . "Found\n"; }

    Which prints:

    AB1/1 : Found AB1/2 : NotFound AB1/5 : Found AB1/6 : NotFound AB1/9 : NotFound AB2/2 : NotFound AB2/5 : Found AB2/6 : NotFound AB2/9 : NotFound AB2/13 : NotFound

    Update: removed the extra brace liverpole found. Copy and Paste... Thanks!

      You've got an extra brace at the end of your code -- but otherwise a good explanation of the where the OP's bug existed.
Re: searching 2 arrays
by liverpole (Monsignor) on Oct 19, 2005 at 12:34 UTC
    I was working on testing my answer, but I see while I was doing that, Zaxo beat me to it!  Here's what I came up with:
    #!/usr/bin/perl -w + # Strict use strict; use warnings; + # Data my @DB = qw( AB1/1 AB1/5 AB2/5); my @Input = qw( AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 +AB2/13 ); + # Main program &show_me("From DB", \@DB); &show_me("From Input", \@Input); + # Make a hash from the DB array, and run the Input list looking for ma +tches my %DB = map { $_ => 1 } @DB; foreach my $input (@Input) { my $result = defined($DB{$input})? "Found": "NotFound"; printf "%s : %s %s\n", $input, $result, $input; } # Subroutines sub show_me() { my ($msg, $pdata) = @_; print "$msg:\n"; map { printf " %s\n", $_ } @$pdata; print "\n"; }
    The point that I would make is, the input array is best handled as an array, because you want to preserve the order when you're checking the existence of entries in it.  But the database array should be converted to a hash, because you want to quickly check it for existence of keys which match the input values.  You certainly don't want to do a nested loop, if for no other reason than, in a case where you have a lot of data, you'll be making the algorithm needlessly slow.
Re: searching 2 arrays
by inman (Curate) on Oct 19, 2005 at 13:19 UTC
    Combine map and grep to produce an array or results.
    my @list_in = qw ( AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 AB2/13); my @list_c = map {my $x = $_; $_ = grep(/$x/,@list_db) ? "$_ Found" : +"$_ Not Found"} @list_in; print "@list_c";
Re: searching 2 arrays
by Rajeshk (Scribe) on Oct 27, 2005 at 11:47 UTC
    Hi,
    	Try this one.
    
    use strict;
    use warnings;
    local $\ = "\n";
    
    my @DB    = qw(AB1/1 AB1/5 AB2/5);
    my @Input = qw(AB1/1 AB1/2 AB1/5 AB1/6 AB1/9 AB2/2 AB2/5 AB2/6 AB2/9 AB2/13);
    
    foreach my $elt(@Input){
    	if(grep(/$elt/, @DB)){
    		print $elt.': Found'
    	}
    	else{
    		print $elt.': Not found';
    	}
    }
    
    Thanks,
    Rajesh.K