CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
Looping through two arrays

 



d1zz13
User

Sep 29, 2004, 7:23 AM

Post #1 of 12 (1350 views)
Looping through two arrays Can't Post

Hi there,

I have two arrays that were obtained from reading in a file, I'm trying to loop through to do a comparison. I want to push the differences into a new array. The code I have that isn't working is:

Code
 $flocation = '/home/domains/dizzie.co.uk/user/htdocs/compare/scompare'; 

open (EXISTING, "<$flocation/existing.txt") || die "Unable to open existing.txt\n $!\n";
@existing = <EXISTING>;
close EXISTING;

open (NEW, "<$flocation/new.txt") || die "Unable to open new.txt\n $!\n";
@new = <NEW>;
close NEW;

while ($existing = <EXISTING>){
foreach $asset(@new){
if ($asset eq $existing){
push(@unique, $asset);
last;
}
}
}

print "Content-type: text/html\n\n";
foreach $new(@unique){
print "$new<br>\n";
}



This is returning nothing, any ideas? I think the while loop is wrong but I'm not sure why.

Any help would be appreciated.

Regards
Rich

If it aint broke then don't try to fix it


davorg
Thaumaturge / Moderator

Sep 29, 2004, 9:16 AM

Post #2 of 12 (1349 views)
Re: [d1zz13] Looping through two arrays [In reply to] Can't Post

You don't have "use warnings" turned on do you? If you turn on "use warnings" then Perl will give you a huge clue to what the problem is.

It's always a very good idea to ask Perl for as much help as possible when writing a program. I always turn on "use strict" and "use warnings" - you should too.

--
Dave Cross, Perl Hacker, Trainer and Writer
http://www.dave.org.uk/
Get more help at Perl Monks


d1zz13
User

Sep 29, 2004, 11:12 AM

Post #3 of 12 (1344 views)
Re: [davorg] Looping through two arrays [In reply to] Can't Post

Dave,

Please see amended code below:


Code
use strict; 
use CGI::Carp qw(fatalsToBrowser);
use vars qw($flocation $existing $asset $new @existing @new @unique);

$flocation = '/home/domains/dizzie.co.uk/user/htdocs/compare/scompare';

open (EXISTING, "<$flocation/existing.txt") || die "Unable to open existing.txt\n $!\n";
@existing = <EXISTING>;
close EXISTING;

open (NEW, "<$flocation/new.txt") || die "Unable to open new.txt\n $!\n";
@new = <NEW>;
close NEW;

while ($existing = <EXISTING>){

foreach $asset(@new){
if ($asset eq $existing){
push(@unique, $asset);
last;
}
}
}

print "Content-type: text/html\n\n";
foreach $new(@unique){
print "$new<br>\n";
}

Now I'm using strict, warnings and carp. No errors now and still no results. Any other ideas?

Rich

Regards
Rich

If it aint broke then don't try to fix it


davorg
Thaumaturge / Moderator

Sep 29, 2004, 3:23 PM

Post #4 of 12 (1342 views)
Re: [d1zz13] Looping through two arrays [In reply to] Can't Post

If you turned "use warnings" on (which you still haven't done) then you would get a warning saying "readline() on unopened filehandle EXISTING at xxx.pl line xx".

What do you think the line

Code
while ($existing = <EXISTING>){

is doing? You have already closed the EXISTING filehandle. You can't read from it.

--
Dave Cross, Perl Hacker, Trainer and Writer
http://www.dave.org.uk/
Get more help at Perl Monks


d1zz13
User

Sep 30, 2004, 2:44 AM

Post #5 of 12 (1339 views)
Re: [davorg] Looping through two arrays [In reply to] Can't Post

Dave, My apologies, I thought you meant the -w switch on the shebang line.

I now have the following and still no joy:

Code
#!/usr/bin/perl -w 

# Help with error messages
use warnings;
use strict;
use CGI::Carp qw(fatalsToBrowser);

# File location
my $flocation = '/home/domains/dizzie.co.uk/user/htdocs/compare/scompare';

# Open the existing list of assets
open (EXISTING, "<$flocation/existing.txt") || die "Unable to open existing.txt\n $!\n";
my @existing = <EXISTING>;
close EXISTING;

# Open the new list of assets
open (NEW, "<$flocation/new.txt") || die "Unable to open new.txt\n $!\n";
my @new = <NEW>;

my @diffs;

# Check each line of the new list and if it's unique add it to the diffs array
while (<NEW>){

foreach my $asset(@existing){
if ($asset ne $_){
push(@diffs, $asset);
}
}
}

close NEW;

# Print the contents of the diffs array
print "Content-type: text/html\n\n";

foreach my $diff(@diffs){
print "$diff<br>\n";
}

Slightly changed and I think looking ok now, no warnings but still no results.

Thanks for your help so far, I appreciate you must think you're banging your head against a brick wall.

Rich


davorg
Thaumaturge / Moderator

Sep 30, 2004, 3:13 AM

Post #6 of 12 (1338 views)
Re: [d1zz13] Looping through two arrays [In reply to] Can't Post

Well, "use warnings" and "-w" _are_ pretty much similar, but I didn't know that you'd turned on "-w" either as you didn't include that line in your previous code example :)

Generally, "use warnings" is prefered to "-w" as it's newer and more flexible.

I think you're looking in the wrong place for your warning messages. You have turned on "fatalsToBrowser" which means that _fatal_ errors are written to the browser. But the messages I am talking about are non-fatal warnings. This are not getting written to your browser, but they will be written to your web server error log. You need to look there for them.

Or, alternatively, looking at the docs for CGI::Carp I see that you can arrange for warnings to be sent as HTML comments to your browser by using code like:


Code
use CGI::Carp qw(fatalsToBrowser warningsToBrowser); 
use CGI qw(:standard);
print header();
warningsToBrowser(1);


(note that you probably want to ensure that you print the Content-type header as soon as possible with this usage)

But anyway, the warning was there (even if you couldn't find it!) And I told you what the warning was in my last message - "readline() on unopened filehandle". I even pointed out which line was causing the error.

So let's look at the code in a bit more detail.

You open the EXISTING filehandle, read in the file and then close the filehandle. You then do the same with the NEW filehandle (but you don't close it). The EXISTING filehandle is closed and the NEW filehandle is open. But (crucially) you have already read all of the data from NEW. The file pointer on that filehandle is at the END of the file. Any further attempt to read data from that filehandle will fail (and return undef).

So far so good.

You then use the <...> operator to read lines of data one at a time from the NEW filehandle. but remember what I said above. The file pointer is at the end of the file. You can't read data from it any more (you should probably be looking at the @new array rather than the NEW filehandle).

But Perl assumes that you are clever than it is. It assumes that you know what you are doing. It simply returns "undef" from the operation - to indicate that there's no data to be read from that filehandle.

So your "while" loop condition returns "undef" - which is "false". So the while loop never executes. No data ever gets into @diffs. So your page displays nothing.

Does that help?

--
Dave Cross, Perl Hacker, Trainer and Writer
http://www.dave.org.uk/
Get more help at Perl Monks


d1zz13
User

Oct 4, 2004, 6:10 AM

Post #7 of 12 (1332 views)
Re: [davorg] Looping through two arrays [In reply to] Can't Post

Dave,

Thanks for all your help, I think I'm almost there. I have the following code now (excuse the fact that I've removed the use strict and the use warnings I appreciate the importance of these now and shall be adding them in shortly:

Code
#!/usr/bin/perl 

# File location
my $flocation = '/home/domains/dizzie.co.uk/user/htdocs/compare/scompare';

# Open the new list of assets to compare
open (NEW, "<$flocation/new.txt") || die "Unable to open new.txt\n $!\n";
@new = <NEW>;
close NEW;

print "Content-type: text/html\n\n";

# Open the existing list of assets to compare against
open (EXISTING, "<$flocation/existing.txt") || die "Unable to open existing.txt\n $!\n";

# Loop through and compare
while (<EXISTING>){
chomp;

foreach $asset (@new){
chomp $asset;
if ($asset eq $_){
print "$asset - Matched<br>\n";
next;
}
else {
print "$asset - Not Matched<br>\n";
}
}
}

close EXISTING;


This produces the following results:

Code
ESW000006 - Matched 
ESW000627 - Not Matched
ESW001032 - Not Matched
ESW004040 - Not Matched
ESW007000 - Not Matched
ESW000006 - Not Matched
ESW000627 - Matched
ESW001032 - Not Matched
ESW004040 - Not Matched
ESW007000 - Not Matched
ESW000006 - Not Matched
ESW000627 - Not Matched
ESW001032 - Matched
ESW004040 - Not Matched
ESW007000 - Not Matched


The result I'm looking for is:

Code
ESW000006 - Matched 
ESW000627 - Matched
ESW001032 - Matched
ESW004040 - Not Matched
ESW007000 - Not Matched


In effect the answers that I want are there but I think that the problem is how I'm looping, I think that what I need to do is exit out of the 'if loop' when the asset is found (be it a 'matched' or a 'not matched') and then run the while loop again.

Any suggestions now?
Thanks again


KevinR
Veteran


Oct 5, 2004, 11:07 PM

Post #8 of 12 (1327 views)
Re: [d1zz13] Looping through two arrays [In reply to] Can't Post

From Perldocs faqs:


How do I compute the difference of two arrays? How do I compute the intersection of two arrays?

Use a hash. Here's code to do both and more. It assumes that each element is unique in a given array:


Code
    @union = @intersection = @difference = (); 
%count = ();
foreach $element (@array1, @array2) { $count{$element}++ }
foreach $element (keys %count) {
push @union, $element;
push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
}


Note that this is the symmetric difference, that is, all elements in either A or in B but not in both. Think of it as an xor operation.

----------------------------------

substitue your arrays and give it a try.


Code
#!/usr/bin/perl  

# File location
my $flocation = '/home/domains/dizzie.co.uk/user/htdocs/compare/scompare';

print "Content-type: text/html\n\n";

# Open the new list of assets to compare
open (NEW, "<$flocation/new.txt") || die "Unable to open new.txt\n $!\n";
@new = <NEW>;
close NEW;

# Open the existing list of assets to compare against
open (EXISTING, "<$flocation/existing.txt") || die "Unable to open existing.txt\n $!\n";
@existing = <EXISTING>;
close EXISTING;

@union = @intersection = @difference = ();
%count = ();
foreach $element (@new, @existing) { $count{$element}++ }
foreach $element (keys %count) {
push @union, $element;
push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
}

print "Intersection:<br>\n";
print "$_<br>\n" for @intersection;
print "<br>Difference:<br>\n";
print "$_<br>\n" for @difference;

-------------------------------------------------


d1zz13
User

Oct 6, 2004, 3:20 AM

Post #9 of 12 (1322 views)
Re: [KevinR] Looping through two arrays [In reply to] Can't Post

Thanks for the reply Kevin. This unfortunately doesn't produce the correct result.

Here's the result I get:

Code
Intersection: 
ESW000006
ESW000627

Difference:
ESW004040
ESW001032
ESW001032
ESW007000

ESW000006, ESW000627 and ESW001032 are in both files, ESW004040 and ESW007000 are only in @new.

I'll read the perldocs and try to understand what you've pasted though. I appreciate the help.


KevinR
Veteran


Oct 7, 2004, 8:54 PM

Post #10 of 12 (1315 views)
Re: [d1zz13] Looping through two arrays [In reply to] Can't Post

I'm still a bit unclear exactly what you are trying to do but I will assume you want to compare the list @new against @existing and see if the elements of @new do or do not already exist. If So you can try something like this:


Code
#!/usr/bin/perl -w  
use strict;

print "Content-type: text/html\n\n";

# File location
my $flocation = '/home/domains/dizzie.co.uk/user/htdocs/compare/scompare';
# Open the new list of assets to compare
open (NEW, "<$flocation/new.txt") || die "Unable to open new.txt\n $!\n";
my @new = <NEW>;
close NEW;

# Open the existing list of assets to compare against
open (EXISTING, "<$flocation/existing.txt") || die "Unable to open existing.txt\n $!\n";
my @existing = <EXISTING>;
close EXISTING;

my %matched = ();

# Loop through and compare
foreach (@new){
chomp;
$matched{$_} = 'not_matched';
for my $i (0 .. $#existing){
chomp $existing[$i];
if ($existing[$i] eq $_){
$matched{$_} = 'matched';
}
}
}

foreach my $keys (sort keys %matched) {
print "$keys = $matched{$keys}<br>\n";
}

-------------------------------------------------


(This post was edited by KevinR on Oct 7, 2004, 11:51 PM)


d1zz13
User

Oct 8, 2004, 5:01 AM

Post #11 of 12 (1308 views)
Re: [KevinR] Looping through two arrays [In reply to] Can't Post

Kevin,

That worked a treat. Just what I was after. To develop further, I think it would probably be best to push the results into two seperate arrays.

Should this be relatively easy to achieve if I re-work the code you've provided?


KevinR
Veteran


Oct 8, 2004, 9:46 AM

Post #12 of 12 (1307 views)
Re: [d1zz13] Looping through two arrays [In reply to] Can't Post

I will have to leave it up to you to determine if using two separate arrays is best in this situation. I'm sure it can be done no problem. Notice how I switched the @new array to be the list you wanted to compare against the file/list , EXISTING or @existing in this case. You were going about it a bit backwards in my estimation.

Give it a try (pushing into two arrays) and if you really get stuck holler for help and someone (eventually) will reply. Make sure you understand all that Dave posted for you, he is the real Guru here, and his advice is worth its weight in gold. Wink
-------------------------------------------------

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives