Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Making a hash of making a hash of an array

by jonnyfolk (Vicar)
on Jun 04, 2003 at 03:54 UTC ( [id://262877]=perlquestion: print w/replies, xml ) Need Help??

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

In order to populate a table in HTML::Template I'm having to go a bit beyond my current knowledge base and have run into some problems.

I have set up and confirmed the template using the example having entered the values into the script. I cannot, however, extract the values from a file to populate the hash. In the code that follows I am opening a flat file and reversing the line order of the file to extract the last 10 records

open (FILE, "$record") or die "can't open $record: $!"; my @record = reverse <FILE>; close FILE; foreach my $line (@record) { ($timestamp, $current_time, $funds, $action, $current_funds) = split " +\t",$line; while ($count < 10){ $member{$timestamp} = [$current_time, $funds, $action, $current_fu +nds]; $count += 1; } } %member = ( $timestamp => [$current_time, $funds, $action, $current_fu +nds]); my $template = HTML::Template->new(filename => 'temp_late.html'); my @loop; # the loop data will be put in here # fill in the loop, sorted by timestamp foreach my $timestamp (sort keys %member) { # get the other data from the data hash my ($current_time, $funds, $action, $current_funds) = @{$member{$t +imestamp}}; # make a new row for this data - the keys are <TMPL_VAR> names # and the values are the values to fill in the template. my %row = ( date => $current_time, amount => $funds, action => $action, total => $current_funds, ); # put this row into the loop by reference push(@loop, \%row); } # call param to fill in the loop with the loop data by reference +. $template->param(member => \@loop); # send the obligatory Content-Type print "Content-Type: text/html\n\n"; # print the template print $template->output;
I'd be grateful If someone could help me to get this right.

Replies are listed 'Best First'.
Re: Making a hash of making a hash of an array
by kabel (Chaplain) on Jun 04, 2003 at 04:58 UTC
    open (FILE, "$record") or die "can't open $record: $!"; my @record = reverse <FILE>; close FILE;

    this is considered bad due to the fact that the file could eventually get large and fill up your memory. have a look at File::ReadBackwards.

    %member = ( $timestamp => [$current_time, $funds, $action, $current_funds]);
    this is 100% wrong, because %member contains afterwards exactly one element, the ten that got into before are lost. you want:

    $member{$timestamp} = [$current_time, $funds, $action, $current_funds];
    then, you really want to use strict; #damn ;) and to use CGI; or an equivalent module. see the tutorials for why.

    update: i just realized that you don't chomp; your input lines. so, the last elem will have a line separator character at its end.

    HTH

    btw i hope formatting is correct ...
      Thanks very much for your input, kabel, the File::ReadBackwards is especially interesting. I am actually using CGI.pm and strict but simply put in a snippet of my script which I thought the most relevant.
(jeffa) Re: Making a hash of making a hash of an array
by jeffa (Bishop) on Jun 04, 2003 at 05:55 UTC
    Interesting problem. I really wish you would have supplied your template as well as your flat text file, as you didn't, i'll have to make up my own. ;) I chose to use Tie::File, even though my first instincts were to use File::ReadBackwards. I chose Tie::File because i'd rather work with an array slice, that's all. There are two lines that print two important datastructures to STDERR -- understanding what you datastructure should look like is the key in getting the hang of loop with HTML::Template. You don't have to use map as i have done, i just wanted to show you that Perl can be quite consise. (Make sure that the entire DATA section is included in the script if you copy this.)
    use strict; use warnings; use Tie::File; use Data::Dumper; use HTML::Template; # part 1: get the last ten lines and store in a 2-D array my @record; tie @record, 'Tie::File', 'record.txt' or die $!; my @last10 = map [split "\t"], @record[map -$_,1..10]; #print STDERR Dumper \@last10; # part 2: create a list of hashes (LoH) for HTML::Template my %hash; my @headers = qw(date amount action total); my @loop = map { @hash{@headers} = @$_[1..@$_]; # exclude timestamp $_ = {%hash} } sort {$a->[0] <=> $b->[0]} @last10; # sort by timestamp #print STDERR Dumper \@loop; # part 3: shove data through template my $tmpl = HTML::Template->new(filehandle => \*DATA); $tmpl->param(member => \@loop); print $tmpl->output; __DATA__ <table> <tr> <th>Date</th> <th>Amount</th> <th>Action</th> <th>Total</th> </tr> <tmpl_loop member> <tr> <td><tmpl_var date></td> <td><tmpl_var amount></td> <td><tmpl_var action></td> <td><tmpl_var total></td> </tr> </tmpl_loop> </table>
    record.txt is a tab delimited file similar this one that i used for testing:
    1054703890	a	b	c	d
    1054703892	e	f	g	h
    1054703894	1	2	3	4
    1054703896	5	6	7	8
    1054703891	A	B	C	D
    1054703893	E	F	G	H
    1054703895	my	how	time	flies
    

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Making a hash of making a hash of an array
by pzbagel (Chaplain) on Jun 04, 2003 at 05:06 UTC

    I believe the logic in your for loop is incorrect, beside kabel's point about wrecking your hash by assigning to it after the loop, the while loop inside the for simply ends up assigning each line of the file to the hash 10 times

    foreach my $line (@record) { ($timestamp, $current_time, $funds, $action, $current_funds) = split " +\t",$line; # The values of $timestamp, $current_time, $funds, $action, $current_f +unds never change in this while loop. while ($count < 10){ $member{$timestamp} = [$current_time, $funds, $action, $current_fu +nds]; $count += 1; } }

    You want something like this:

    for my $line (@record[0..9]) { ($timestamp, $current_time, $funds, $action, $current_funds) = split + "\t",$line; $member{$timestamp} = [$current_time, $funds, $action, $current_fund +s]; }

    HTH

      Thanks, pzbagel.

      After so many hours of trying to make it work you cannot imagine the relief I felt when I pasted your lines in and a table appeared as if by magic.

      I had thought that the

      foreach my $line (@record)
      would extract each line and place it in the hash. That is obviously not the case and I don't understand why. If you have a moment to explain i'd be grateful.

        Actually, that's exactly what it was doing. The way your code was written, it would have inserted all the lines from the file into the hash(granted the nested while was reinserting that same hash values 10 times each). It was the line immediately after the foreach loop, as kabel already pointed out, which was resetting your your hash to just one key/value pair:

        # This is the culprit! %member = ( $timestamp => [$current_time, $funds, $action, $current_fu +nds]);

        That line was resetting the %member hash to the last values of $timestamp, et al. Removing that line would leave you with a hash with every line split and inserted. Now since you only wanted the last 10 lines, the for loop code I posted simply took an array slice from 0-9 and iterated over those, effectively letting the loop parse only the last 10 lines of the file.

        Hope that clears things up a bit.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://262877]
Approved by krujos
Front-paged by gmax
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-04-19 02:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found