473,406 Members | 2,345 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

How to pass an array and hash to a subroutine

Hi everyone,

I'm working on a script that takes in a certain file A and compares it against several other files and prints any lines that exist in A but not in the other files. I was able to code a script so that File A is compared against 4 other files.

However, to improve code readability and compare File A against a variable number of files, I am trying to make the actual comparison process a subroutine so that it can be called the necessary number of times.

So my code takes in the first file and reads in every line, and makes a hash with the line as the key and its value 1. It then takes in each of the other files one by one and reads in the line from the file. The line that's read is used in a conditional statement to see if that key exists, and if it does, increments the value of that key by 1. In order to print only the differences of file A, I only print the hash keys that have values of 1.

The problem with my code is that although the array and hash is passed to the subroutine "compare," when the check to see if the key exists, the statement is always false for some reason. I believe the problem has something to do with the passing of the arguments into the subroutine because although I can access the array and hash successfully in the subroutine, the conditional statement is never satisfied.

So, I'm not sure if it's a perl syntax error or programmer but any help on this would be greatly appreciated.


Expand|Select|Wrap|Line Numbers
  1. %var = ();
  2. while ($ln1 = <FH>) {                                              # creates keys with first element of string (test name with no arguments)
  3.   @ln1 = split /\s+/, $ln1;                                        
  4.   my $ln1 = shift @ln1;
  5.   $var{$ln1}++;                                                       # value of each key set to 1
  6.  
  7. @files = (file1, file2, file3, file4);
  8. foreach (@files) {
  9.   open FILE, $_ or die "------ Could not open $_ testlist. \n"; 
  10.   @line = <FILE>;
  11.   close FILE;
  12.   &compare(@line, %var);
  13. }
  14.  
  15. sub compare (\@\%){
  16.   my @line = shift @_;
  17.   my %var = shift @_;
  18.   foreach $ln (@line) {
  19.     if (exists($var{$ln})) {
  20.       $var{$ln}++;
  21.     }   
  22.   }
  23. }
  24.  
  25.  
Feb 19 '11 #1

✓ answered by miller

In reply to your private message, it appears you weren't chomp the lines of the file you were attempting to compare to. This meant that all the lines ended with a return character, and all of the words you tried to match did not contain such.

The following includes some cleaning up I personally would make to your code, but it probably still needs work:

Expand|Select|Wrap|Line Numbers
  1. #! /usr/bin/perl -w
  2.  
  3. use strict;
  4.  
  5. my $fileA = shift @ARGV;
  6. my $fileB = shift @ARGV;
  7.  
  8. my $output = "output.txt";
  9.  
  10. # creates keys with first element of string (test name with no arguments)
  11.  
  12. our %var = ();
  13. open my $fh, $fileA or die "$fileA: $!";
  14. while (my $line = <$fh>) {
  15.     if ($line =~ /(\S+)/) {
  16.         $var{$1} = 0; # Initialize
  17.     }
  18. }
  19. close $fh;
  20.  
  21. my @files = ($fileB);
  22. foreach (@files) {
  23.     my @lines = ();
  24.     open my $fh, $_ or die "$_: $!";
  25.     while (<$fh>) {
  26.         chomp;
  27.         push @lines, $_;
  28.     }
  29.     close $fh;
  30.  
  31.     compare(\%var, \@lines);
  32. }
  33.  
  34. sub compare {
  35.     my $hashref = shift;
  36.     my $arrayref = shift;
  37.     foreach my $ln (@$arrayref) {
  38.         if (exists $hashref->{$ln}) {
  39.             $hashref->{$ln}++;
  40.             print "Matching keys are: $ln \n";
  41.         }
  42.     }
  43.     return;
  44. }
  45.  
  46. #open my $oh, '>', $output or die "$output: $!";
  47. while (my ($key, $val) = each %var) {
  48.     if ($val == 1) {
  49.         print "$key is equal\n";
  50.     } else {
  51.         print "$key, $val is different\n";
  52.     }
  53. }
  54.  

10 5892
I havn't worked with Perl in forever since switching to PHP but you appear not be returning any value at all.You need to return the value and evaluate it or return TRUE or FALSE in your subroutine
Feb 20 '11 #2
Hi,
Well the conditional statement in line 20:
if (exists($var{$ln}))
is never successful and so, the values of the hashes are never incremented. So I was wondering if the syntax in lines 13 and 16-18 for passing the array and hash is correct, which I believe has nothing to do with the subroutine returning a value.
Feb 20 '11 #3
miller
1,089 Expert 1GB
To pass a hash or an array to a subroutine you must pass it by reference. Alternatively, you can also add a prototype to your sub, but you will still be passing by reference ultimately.

Then you simply have to decide if you want to dereference your parameters, or if you want to edit the copy of the passed data structures.

My advice to you is to avoid prototypes entirely, and just pass your variables by reference explicitly. Take a look at the below perl code that does a similar thing to what you are on some fake data.

Expand|Select|Wrap|Line Numbers
  1. use Data::Dumper;
  2.  
  3. use strict;
  4.  
  5. my %hash = map {$_ => 1} qw(e f g h i j k);
  6.  
  7. compare(\%hash, qw(a d f));
  8. compare(\%hash, qw(e h i));
  9. compare(\%hash, qw(c d e));
  10. compare(\%hash, qw(g h i));
  11.  
  12. print Dumper(\%hash);
  13.  
  14. sub compare {
  15.     my $hash = shift;
  16.     my @array = @_;
  17.  
  18.     foreach (@array) {
  19.         if (exists $hash->{$_}) {
  20.             $hash->{$_}++;
  21.         }
  22.     }
  23.  
  24.     return;
  25. }
  26.  
  27. 1;
  28.  
  29. __END__
Also note the included use strict; statement at the beginning of my code. All perl coders should include this in the scripts, as it will save you so much time letting perl do the syntax error checking for you.

- Miller
Feb 20 '11 #4
Thanks Miller.

I am trying to implement the sample code for referencing and dereferencing but I run into the error of
Expand|Select|Wrap|Line Numbers
  1. Use of uninitialized value in hash element
  2.  
at line 5 of the code I posted. I initialize the hash in line 1 so I don't understand why my code would return this error.

Once again, thank you for the help.
Feb 22 '11 #5
miller
1,089 Expert 1GB
Did you get the sample code I provided you working? Yes?

Do you understand where your problem was before?

If you are now changing your code, where is the new version with the error, and exactly what line number is the error reported on?

Finally did you add "use strict;" to the beginning of your program and therefore fix all the variable declarations with "my"? If you haven't done this, then you haven't done your due diligence yet. This is the number 1 thing you can do to get perl to help you find basic syntax errors in your program. Do this before anything else.

- Miller
Feb 22 '11 #6
Sorry I was trying to delete that previous post because that warning is irrelevant to this problem but I didn't know how.

Anyway, I did implement your method of passing the hash and array and also your variable naming convention. I checked to see if I could modify the values of the hash in the subroutine and everything checks out. However, my problem in your code equivalent would be at line 19 and is still the same as before

Expand|Select|Wrap|Line Numbers
  1.      if (exists $hash->{$_}) { 
  2.  
This line is never true and so lines that file A and other files never get omitted from being printed to the output. If I manually select a key for the hash, the value is a 1 (which is correct) and if print $_, I can see that that line is from the file. But, if I try to print a value of the hash using $_ by printing $var->{$_}, I receive the error
Expand|Select|Wrap|Line Numbers
  1. Use of uninitialized value in concatenation (.) or string
  2.  
Feb 22 '11 #7
miller
1,089 Expert 1GB
Where is all of your newly updated code?
Feb 22 '11 #8
Expand|Select|Wrap|Line Numbers
  1. my @files = ($file1, $file2, $file3, $file4);
  2. foreach (@files) {
  3.   open FILE, $_ or die "------ Could not open $_ testlist. \n"; 
  4.   my @line = <FILE>;
  5.   close FILE;
  6.   &compare(\%var,\@line);
  7. }
  8.  
  9. sub compare {
  10.   my $var = shift;
  11.   my @line = @_;
  12.   foreach my $ln (@line) {
  13.     if (exists $var->{$ln}) {
  14.       $var->{$ln}++;
  15.       print "Matching keys are: $ln \n";
  16.     }  
  17.   }
  18.   return;
  19. }
  20.  
Sorry, I realize that the pass by reference for the array isn't working. I don't understand how the 2nd line of sub compare would pass the value of the array. (using your sample code as a reference)
Feb 22 '11 #9
miller
1,089 Expert 1GB
Easily fixed. You just need to study references and their associated syntax some more.

Either pass the array as a list of parameters like so:

Expand|Select|Wrap|Line Numbers
  1. my @files = ($file1, $file2, $file3, $file4);
  2. foreach (@files) {
  3.     open FILE, $_ or die "------ Could not open $_ testlist. \n"; 
  4.     my @line = <FILE>;
  5.     close FILE;
  6.     compare(\%var, @line);
  7. }
  8.  
  9. sub compare {
  10.     my $hashref = shift;
  11.     my @line = @_;
  12.     foreach my $ln (@line) {
  13.         if (exists $hashref->{$ln}) {
  14.             $hashref->{$ln}++;
  15.             print "Matching keys are: $ln \n";
  16.         }  
  17.     }
  18.     return;
  19. }
Or pass it by reference like so:

Expand|Select|Wrap|Line Numbers
  1. my @files = ($file1, $file2, $file3, $file4);
  2. foreach (@files) {
  3.     open FILE, $_ or die "------ Could not open $_ testlist. \n"; 
  4.     my @line = <FILE>;
  5.     close FILE;
  6.     compare(\%var, \@line);
  7. }
  8.  
  9. sub compare {
  10.     my $hashref = shift;
  11.     my $arrayref = shift;
  12.  
  13.     foreach my $ln (@$arrayref) {
  14.         if (exists $hashref->{$ln}) {
  15.             $hashref->{$ln}++;
  16.             print "Matching keys are: $ln \n";
  17.         }  
  18.     }
  19.     return;
  20. }
Either method works. Just note that passing a reference sometimes implies that you want to do operations that modify that very data structure. However, it also can be done for simple efficiency, as this way you're not duplicating the data structure inside the subroutine.

- Miller
Feb 22 '11 #10
miller
1,089 Expert 1GB
In reply to your private message, it appears you weren't chomp the lines of the file you were attempting to compare to. This meant that all the lines ended with a return character, and all of the words you tried to match did not contain such.

The following includes some cleaning up I personally would make to your code, but it probably still needs work:

Expand|Select|Wrap|Line Numbers
  1. #! /usr/bin/perl -w
  2.  
  3. use strict;
  4.  
  5. my $fileA = shift @ARGV;
  6. my $fileB = shift @ARGV;
  7.  
  8. my $output = "output.txt";
  9.  
  10. # creates keys with first element of string (test name with no arguments)
  11.  
  12. our %var = ();
  13. open my $fh, $fileA or die "$fileA: $!";
  14. while (my $line = <$fh>) {
  15.     if ($line =~ /(\S+)/) {
  16.         $var{$1} = 0; # Initialize
  17.     }
  18. }
  19. close $fh;
  20.  
  21. my @files = ($fileB);
  22. foreach (@files) {
  23.     my @lines = ();
  24.     open my $fh, $_ or die "$_: $!";
  25.     while (<$fh>) {
  26.         chomp;
  27.         push @lines, $_;
  28.     }
  29.     close $fh;
  30.  
  31.     compare(\%var, \@lines);
  32. }
  33.  
  34. sub compare {
  35.     my $hashref = shift;
  36.     my $arrayref = shift;
  37.     foreach my $ln (@$arrayref) {
  38.         if (exists $hashref->{$ln}) {
  39.             $hashref->{$ln}++;
  40.             print "Matching keys are: $ln \n";
  41.         }
  42.     }
  43.     return;
  44. }
  45.  
  46. #open my $oh, '>', $output or die "$output: $!";
  47. while (my ($key, $val) = each %var) {
  48.     if ($val == 1) {
  49.         print "$key is equal\n";
  50.     } else {
  51.         print "$key, $val is different\n";
  52.     }
  53. }
  54.  
Feb 23 '11 #11

Sign in to post your reply or Sign up for a free account.

Similar topics

5
by: John | last post by:
I would like to pass array variables between URLs but I don't know how. I know its possible with sessions, but sessions can become useless if cookies are disabled. I have unsuccessfully tried...
2
by: Anders Eriksson | last post by:
Hello! I'm a beginner at PHP and I wonder how I would go about to read a textfile into an array(hash). The textfile looks like this: A/S=age/sex? A/S/L=age/sex/location? AA=alcoholics...
41
by: Berk Birand | last post by:
Hi, I am just learning about the array/pointer duality in C/C++. I couldn't help wondering, is there a way to pass an array by value? It seems like the only way to do is to pass it by...
5
by: wilson | last post by:
Dear all, In this time, I want to pass array to function. What should I declare the parameter in the function?i int array or int array? Which one is correct? ...
3
by: Boni | last post by:
Dear all, I have written a C++ com object: STDMETHOD(myfunc)(INT* array) Now I need to pass an array from c# to com. I generated a COM interop and it the signature of the function is ...
6
by: Wijaya Edward | last post by:
Hi, How can I pass Array, Hash, and a plain variable in to a function at the same time. I come from Perl. Where as you probably know it is done like this: sub myfunc {
0
by: Jagdish | last post by:
Hello, Every body I have recently joined this group and I want to know how to pass Array of string to a Com object function.. Actually I want to pass String array from VB6 to Com Interface...
3
by: keithl | last post by:
Hi (again !) I posted a question yesterday just for info on hashrefs which I have read and it makes sense. Putting this into proactive though has now toally confused me ! I have an XML file...
2
by: neehakale | last post by:
If we have to pass values to the hash table from file then what is the way???pls tel me...we have to use array,but then how we divide this array to key and value filds.pls tel me m new to this...
6
by: johntology | last post by:
Hello, I've made a two dimensional array using references, which I gather is the only way to do it in Perl. I now need to pass each interior array to a subroutine for processing and can't quite...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.