Newbie Hash question.

W

Wondering

I'm teaching myslf Perl using the Camel book. I'm trying to load some
data, and I think a hash is a good solution for mapping values. I did
one simple hash in the program, and it worked as expected. Now I have a
larger lookup table, and rather than write it into the program, I'd
prefer to store the lookup table in a text file, and load it at
runtime. I've tried a couple of different methods, and I thought I had
it working at one point. Then, a couple of days went by, and now I
can't get what I want at all. I separated the code for that hash into a
separate Perl script so I could test it, and nothing I've tried (book
in hand) has given the result I would like. I expect it's something
simple a newbie like me would miss.

Here's an abbreviated version of the table from the text file (filename
is "sufhash"):

'149' => ' x'
'150' => ' y'
'151' => ' z'
'152' => 'AA'

I've also written this file with commas at the end of each line, but
this is the current iteration.

Here's the Perl script I'm using to test:
#!/usr/contrib/bin/perl

%sufmap = (`cat /"full path"/sufhash`) ;
$hashcnt = (keys(%sufmap)) ;
# @keys = keys %sufmap ;
# @val = values %sufmap ;
# while (@keys) {
# print pop(@keys), '=', pop(@val), "\n" ;
# }
# foreach $key (sort keys %sufmap) {
# print $key. '='. $sufmap{$key}, "\n";
# $sleep = (`sleep 2`) ;
# }
while (($key,$value) = each %sufmap) {
print "$key = $value\n" ;
}

You can see that I've tried (and commented out) several ways of
displaying the contents of the hash. As near as I can tell, each method
of displaying works about as well as any other, so I suspect the
problem is in the way I've written the text file the hash is loaded
from. What I get is a key/value pair on a line, followed by a "=" sign,
and another key/value pair on the next line. What I expect is a key,
the "=" sign, and the corresponding value all on one line. I'm not so
particular about the output of the test script, if I can get the hash
to work in the program.

Thanks to all who take the time to read all this, and a bigger thanks
to those who respond. I'll be happy to provide additional info as
requested.
 
M

Martin Kissner

Wondering wrote :
[...]
What I get is a key/value pair on a line, followed by a "=" sign,
and another key/value pair on the next line. What I expect is a key,
the "=" sign, and the corresponding value all on one line. I'm not so
particular about the output of the test script, if I can get the hash
to work in the program.
I'm a newbie, too and I have written a script last week which also has
to populate a hash from a simple file.

I'll provide a snipped of it here in order to give you a hint to a
possible solution and also to ask the regulars to take a look on it
whether it can be optimized.

The text file is formated like this:

name1:url1
name2:url2

--- snip ---
#!/usr/bin/perl

use warnings;
use strict;
use HTML::Template;

my $file = "/path/file";

# [...]

my (%row, $row, $name, $url);
open LINKS, "<", $file or die "Can not open $file: $!";
while ($line = <LINKS>)
{
my @line = split (":", $line);
$name = $line[0];
$url = $line[1];

# this is how I get the hash populated with key/value pairs
$row{$name} = $url;
}

# [...]

--- snap ---

HTH
Martin
 
I

ioneabu

Here's an abbreviated version of the table from the text file
(filename
is "sufhash"):

'149' => ' x'
'150' => ' y'
'151' => ' z'
'152' => 'AA'

I've also written this file with commas at the end of each line, but
this is the current iteration.

Here's the Perl script I'm using to test:
#!/usr/contrib/bin/perl

%sufmap = (`cat /"full path"/sufhash`) ;
$hashcnt = (keys(%sufmap)) ;
# @keys = keys %sufmap ;
# @val = values %sufmap ;
# while (@keys) {
# print pop(@keys), '=', pop(@val), "\n" ;
# }
# foreach $key (sort keys %sufmap) {
# print $key. '='. $sufmap{$key}, "\n";
# $sleep = (`sleep 2`) ;
# }
while (($key,$value) = each %sufmap) {
print "$key = $value\n" ;
}

I named my data file test.dat. Here is my first program which attempts
to do it using cat as in your example. I am on a Windows machine now,
so I had to run it under Cygwin.

#!/usr/bin/perl

use strict;
use warnings;

my $a = `cat ./test.dat` ;
$a =~ s/'\s*'/','/g;
my %sufmap;
$a = '%sufmap = ('.$a.')';
eval($a);
while ((my $key,my $value) = each %sufmap) {
print "$key = $value\n" ;
}

Probably most people here would not like this way of solving the
problem, so I did it another way:

#!/usr/bin/perl

use strict;
use warnings;

open my $file, './test.dat' or die "cannot open: $!";
my @a = <$file> ;
my %sufmap = map{split '=>', $_} @a;
print "$_ = ", $sufmap{$_} for keys %sufmap;

You could probably simplify the program if your data was stored in a
more sensible format, maybe:

149
x
150
y
151
z
152
AA

I recommend 'Learning Perl' to pick up the basics.

wana
 
T

Tad McClellan

Martin Kissner said:
I'll provide a snipped of it here in order to give you a hint to a
possible solution and also to ask the regulars to take a look on it
whether it can be optimized.

The text file is formated like this:

name1:url1
name2:url2

--- snip ---
#!/usr/bin/perl

use warnings;
use strict;
use HTML::Template;


Your code does not make use of that module, so why include it?

my (%row, $row, $name, $url);


You should declare your variables in the *smallest possible* scope.

You never use the $row variable, so why declare it?

open LINKS, "<", $file or die "Can not open $file: $!";
while ($line = <LINKS>)

{
my @line = split (":", $line);
$name = $line[0];
$url = $line[1];


You can use a "list assignment" and do away with the temporary variable.

A pattern match should *look like* a pattern match.

my($name, $url) = split (/:/, $line);
 
J

jl_post

Wondering said:
Now I have a larger lookup table, and rather than write
it into the program, I'd prefer to store the lookup table
in a text file, and load it at runtime.


You might consider giving the Data::Dumper module a shot. It's a
standard Perl module, so you should already have it. Here's an example
of how to use it:


File "hashOut.pl" outputs a hash to STDOUT:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash = (cat => 'mammal', dog => 'mammal', sparrow => 'bird');
my $output = Dumper \%hash;
$output =~ s/^\$VAR1 = //; # remove "$VAR1 = "
print $output;
__END__


File "hashIn.pl" reads text from STDIN and forms a hash from it:

#!/usr/bin/perl
use strict;
use warnings;
# Read the input data into $text:
my $text = join('', <>);
# Reconstruct the %hash:
my %hash = %{ eval($text) };
# Print out %hash contents:
print " $_ => $hash{$_}\n" foreach sort keys %hash;
__END__


When you have these two files, you can run them like this:

perl hashOut.pl | perl hashIn.pl

or with an intermediate text file, like this:

perl hashOut.pl > data.txt
perl hashIn.pl < data.txt

Then you will see hashIn.pl print out the hash it got from STDIN.

According to "perldoc Data::Dumper", the string returned from the
Dumper() function is suitable for eval()ing. Therefore, I print out
the Dumper() value of a hash reference in hashOut.pl, then read it back
into a string and eval() that string to get a hash in hashIn.pl .

What's nice about the Data::Dumper module is that it should also be
able to handle nested hashes, arrays, and even Perl objects (blessed
references).

I hope this helps.

-- Jean-Luc
 
M

Martin Kissner

Tad McClellan wrote :
Your code does not make use of that module, so why include it?

I have shortened the script; I should have deleted this line, too.
You should declare your variables in the *smallest possible* scope.

I wouldn't be able to use %row, $name and $url outside the while loop if I
would not declare them here, would I?
You never use the $row variable, so why declare it?

I use it in the deleted part; should have deleted it here, too.
open LINKS, "<", $file or die "Can not open $file: $!";
while ($line = <LINKS>)

while ( my $line = said:
{
my @line = split (":", $line);
$name = $line[0];
$url = $line[1];

You can use a "list assignment" and do away with the temporary variable.

A pattern match should *look like* a pattern match.

my($name, $url) = split (/:/, $line);

Good hint.

Thank you for your feedback
It makes me think about the general design of my script, because I
guess I could process the hash within the while loop.
Then again it would be sufficient to declare the variables within the
loop.

Best regards
Martin
 
A

Anno Siegel

Martin Kissner said:
Tad McClellan wrote :

I have shortened the script; I should have deleted this line, too.


I wouldn't be able to use %row, $name and $url outside the while loop if I
would not declare them here, would I?

You only want to use %row outside the loop. $name and $url can (and
should) be local to the loop body. That's what *smallest possible* means.

Anno
 
A

Anno Siegel

Martin Kissner said:
Tad McClellan wrote :

I have shortened the script; I should have deleted this line, too.


I wouldn't be able to use %row, $name and $url outside the while loop if I
would not declare them here, would I?

You only want to use %row outside the loop. $name and $url can (and
should) be local to the loop body. That's what *smallest possible* means.

Oh, and the lines should be chomped before splitting them.

Anno
 
M

Martin Kissner

Anno Siegel wrote :
You only want to use %row outside the loop. $name and $url can (and
should) be local to the loop body. That's what *smallest possible* means.

Yes, for the hash this is certanly true.
I reused the varibales, but for posting I should have rewritten the
whole script to avoid this confusion.
Sorry for not doing that.
Oh, and the lines should be chomped before splitting them.

They probably usually should.
In my special case it doesn't matter.

Best regards
Martin
 
A

Anno Siegel

Martin Kissner said:
Anno Siegel wrote :

Yes, for the hash this is certanly true.
I reused the varibales, but for posting I should have rewritten the
whole script to avoid this confusion.
Sorry for not doing that.

You should have re-written it anyway.

Extending the scope of variables for re-use is not a good idea. The
small-scope rule is for the benefit of your reader. If you re-use
variables all over the place, the reader must make sure that the value
at one place doesn't have an effect at another, intentional or not.
With small scopes, far-reaching effects can't happen. If that means
you have to declare the same variables in multiple places, do so.
They probably usually should.
In my special case it doesn't matter.

Not now, but who knows. Spurious trailing newlines are baffling people
all the time, it is best not to have them even if they "don't hurt".

Anno
 
M

Martin Kissner

Anno Siegel wrote :
You should have re-written it anyway.

In fact, I did due to the help provided in this wonderful group.
Just didn't want to bother everybody too much by posting it again.
Extending the scope of variables for re-use is not a good idea. The
small-scope rule is for the benefit of your reader. If you re-use
variables all over the place, the reader must make sure that the value
at one place doesn't have an effect at another, intentional or not.
With small scopes, far-reaching effects can't happen. If that means
you have to declare the same variables in multiple places, do so.

I will keep that in mind.
Actually it was an exception, because I unnecessarily processed the same
hash outside the while loop.
After all I reduced the number of code lines of this part of the script
from about 30 to 12 and limited the scope of variables as far as I
could.
Not now, but who knows. Spurious trailing newlines are baffling people
all the time, it is best not to have them even if they "don't hurt".

I'll keep this in mind, too.
Thanks for your feedback.
Martin
 
W

Wondering

The responses have been most helpful, and I have not only solved my
immediate problem, but I have a much better understanding of hashes in
general. Thanks all!
 
M

Martin Kissner

Wondering wrote :
The responses have been most helpful, and I have not only solved my
immediate problem, but I have a much better understanding of hashes in
general. Thanks all!
Good: You respond to some helpful advice you have been given.
Bad: Nobody knows, what you are talking about, since you didn't quote.

Please read: http://learn.to/quote
Thank you!
Martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top