manipulating a hash of hashes

A

Aaron DeLoach

Hi all,

I have the following HoH:

my %payment_methods =
(
"Credit Cards"=>{
"ae" => "American Express",
"di" => "Discover",
"vi" => "Visa",
"jc" => "JCB",
"dc" => "Diners Club",
"mc" => "MasterCard",
},
"Other"=>{
"mo" => "Money Order",
"po" => "Purchase Order",
},
"Checks"=>{
"pe" => "Personal Check",
"co" => "Company Check",
"ca" => "Cashiers Check",
"ce" => "Certified Check",
},
);

I would like to construct an html table of the data as follows. I don't need
help with the html, just the ability to produce the data in column/row
format.

(hash names)
row1 Credit Cards | Other | Check
(now values)
row2 American express | Money Order | Company Check
row3 Discover | Purchase Order | Cashiers Check
row4 Visa | | Certified Check
row5 JCB | |
row6 Diners Club | |

and so on...

As you can see from the example data ends in different columns as the hash
runs out of values. Any assistance would be greatly appreciated.

Regards
Aaron
 
P

Paul Lalli

Hi all,

I have the following HoH:

my %payment_methods =
(
"Credit Cards"=>{
"ae" => "American Express",
"di" => "Discover",
"vi" => "Visa",
"jc" => "JCB",
"dc" => "Diners Club",
"mc" => "MasterCard",
},
"Other"=>{
"mo" => "Money Order",
"po" => "Purchase Order",
},
"Checks"=>{
"pe" => "Personal Check",
"co" => "Company Check",
"ca" => "Cashiers Check",
"ce" => "Certified Check",
},
);

I would like to construct an html table of the data as follows. I don't need
help with the html, just the ability to produce the data in column/row
format.

(hash names)
row1 Credit Cards | Other | Check
(now values)
row2 American express | Money Order | Company Check
row3 Discover | Purchase Order | Cashiers Check
row4 Visa | | Certified Check
row5 JCB | |
row6 Diners Club | |

and so on...

As you can see from the example data ends in different columns as the hash
runs out of values. Any assistance would be greatly appreciated.


Don't try to do what's already been done before. Use a module that's
already written, like HTML::Table, available on CPAN:

#!/usr/bin/perl
use strict;
use warnings;
use HTML::Table
my %payment_methods =
(
"Credit Cards"=>{
"ae" => "American Express",
"di" => "Discover",
"vi" => "Visa",
"jc" => "JCB",
"dc" => "Diners Club",
"mc" => "MasterCard",
},
"Other"=>{
"mo" => "Money Order",
"po" => "Purchase Order",
},
"Checks"=>{
"pe" => "Personal Check",
"co" => "Company Check",
"ca" => "Cashiers Check",
"ce" => "Certified Check",
},
);

my $table = new HTML::Table;
foreach $type (keys %payment_methods) {
$table->addCol($type, values(%{$payment_methods{$type}}));
}

print $table->getTable, "\n";

__END__

(note that formatting, for example the column headings, is left as an
excercise to the reader. It should be possible once you've read the
documentation for HTML::Table)

Paul Lalli
 
J

Jeff 'japhy' Pinyan

[posted & mailed]

On Wed, 7 Jul 2004, Aaron DeLoach wrote:

[reformatted]
Credit | Other | Check
===========================================
AmEx | Money Order | Company Check
Disc | Purchase Order | Cashiers Check
Visa | | Certified Check
JCB | | Personal Check
Diner | |
As you can see from the example data ends in different columns as the hash
runs out of values. Any assistance would be greatly appreciated.

I would first get the data out of the hashrefs:

my @data = map [values %$_], values %payment_methods;

This gets us from:

my %payment_methods = (
"Credit Cards" => {
"ae" => "American Express",
"di" => "Discover",
"vi" => "Visa",
"jc" => "JCB",
"dc" => "Diners Club",
"mc" => "MasterCard",
},
"Other"=>{
"mo" => "Money Order",
"po" => "Purchase Order",
},
"Checks"=>{
"pe" => "Personal Check",
"co" => "Company Check",
"ca" => "Cashiers Check",
"ce" => "Certified Check",
},
);

to this:

@data = (
[ "American Express", "Discover", "Visa", ... ],
[ "Money Order", "Purchase Order" ],
[ "Personal Check", "Company Check", ... ],
);

though not necessarily in those orders. Now we need to know the dimension
of the largest hashref (now arrayref):

my $max = 0;
for (@data) { $max = @$_ if $max < @$_ }

Now we can do this in a naive manner or a more cunning manner:

print "<table>\n";
for my $i (0 .. $max-1) {
print "<tr>\n";
for (@data) {
print "<td>$_->[$i]</td>";
}
print "\n</tr>\n";
}
print "</table>\n";

That's naive, because you'll be getting empty cells whenever an arrayref
"runs out" of elements. The more cunning way is to insert "rowspan"
attributes when an array is on its last element, and then to skip that
array afterwards.

print "<table>\n";
for my $i (0 .. $max-1) {
print "<tr>\n";
for (@data) {
# skip if we're past the end
next if $#$_ < $i;

# if this is the last element, make its rowspan bigger
my $span = ($#$_ == $i) ? $max - $#$_ : 1;

print "<td rowspan=$span valign='top'>$_->[$i]</td>";
}
print "\n</tr>\n";
}
print "</table>\n";

--
Jeff "japhy" Pinyan % How can we ever be the sold short or
RPI Acacia Brother #734 % the cheated, we who for every service
RPI Corporation Secretary % have long ago been overpaid?
http://japhy.perlmonk.org/ %
http://www.perlmonks.org/ % -- Meister Eckhart
 
B

Brad Baxter

Hi all,

I have the following HoH:

my %payment_methods =
(
"Credit Cards"=>{
"ae" => "American Express",
"di" => "Discover",
"vi" => "Visa",
"jc" => "JCB",
"dc" => "Diners Club",
"mc" => "MasterCard",
},
"Other"=>{
"mo" => "Money Order",
"po" => "Purchase Order",
},
"Checks"=>{
"pe" => "Personal Check",
"co" => "Company Check",
"ca" => "Cashiers Check",
"ce" => "Certified Check",
},
);

I would like to construct an html table of the data as follows. I don't need
help with the html, just the ability to produce the data in column/row
format.

(hash names)
row1 Credit Cards | Other | Check
(now values)
row2 American express | Money Order | Company Check
row3 Discover | Purchase Order | Cashiers Check
row4 Visa | | Certified Check
row5 JCB | |
row6 Diners Club | |

and so on...

As you can see from the example data ends in different columns as the hash
runs out of values. Any assistance would be greatly appreciated.

Regards
Aaron

Below is another solution using a module. I cheated and used 'sort' in a
couple of places. If you really want the order you show above, then
replace sort with a different method. :)


use Array::Each;

my @headings = sort keys %payment_methods;

my $set = Array::Each->new(
set => [ map [sort values %$_],
@payment_methods{ @headings }, ],
undef => '&nbsp;',
bound => 0,
count => 1,
);

print
"<table border='1'>\n",
"<tr> <td></td>",
map( "<th>$_</th> ", @headings ),
"</tr>\n";
while( my @row = $set->each ) {
printf "<tr> <td>row%d.</td> ", pop @row;
print map( "<td>$_</td> ", @row ), "</tr>\n";
}
print "</table>\n";



Regards,

Brad
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top