Thank you, I'm glad we were able to get it working for you. I've learned a lot about style from this site too. By the way, I noticed a mistake: I was using too long a length in my substr() calls, so I was treating seconds as minutes and minutes as hours. I've corrected that in the version below.
On making the column widths dynamic, think about what you'll need to do. After getting all the values into your hash tables, you'll need to loop through them by column, finding the length of each value and saving the largest length found somewhere, matched to that column. We can save them as the values of our %db hash, since we just had '1' placeholders there before. So the keys of %db will still be the database names (and column headers), but later the values will become the column widths.
This is a bit complicated, and we need to do it twice, once for the hour report and once for the minute report, so I'll make it a subroutine. I pass the main hash (%h or %m) and the %db hash to it as references. I also pass a ref to the @db array of database names, so the subroutine doesn't have to re-get the keys from %db. Since the top-level keys of the hash are the datetimes, I have to loop through those first, then inside that I loop through the database names, decide which length is longer -- the one already saved for that column, or the length of the current item -- and save that in the database name hash. When it's finished, my main program can get the lengths for each column from the values in %db.
sub set_column_widths {
my $h = shift; # reference to hash table of speeds, keyed
+by datetime, then by database
my $databases = shift; # reference to hash of database names, wher
+e we will set the width values
my $names = shift; # ref to array of database names
# (so we don't need to call keys on $da
+tabases repeatedly)
for my $key (keys %$h){
for my $db (@$names){
my $l = length $h->{$key}{$db}; # get length of this item
+ in the hash table
# set column width to the
+ widest length
$databases->{$db} = $databases->{$db} > $l ? $databases->{
+$db} : $l;
}
}
for my $db (@$names){ # check the width of the database names the
+mselves too
my $l = length $db;
$databases->{$db} = $databases->{$db} > $l ? $databases->{$db}
+ : $l;
}
}
Now I just need to call that before printing each report, like this:
set_column_widths(\%h, \%db, \@db);
Now when it's time to print out the columns, we can get the width and use it in the printf() statements where I previously hard-coded 11. For instance, in this line which prints the database names:
printf "%11s", $_ for (@db);
# replace 11 with $db{$_}, the saved width for this column, and
# stick a space between columns
printf " %$db{$_}s", $_ for (@db);
You'll need to make the same change in the other printf() statement, and then you should have dynamic-width columns. Here's the full script with these changes, in case it's not clear where I made them, plus the fixes for my substr() length mistake:
#!/usr/bin/env perl
use 5.010; use strict; use warnings;
my %h; my %m; my %db; # per-hour hash, per-minute hash, database names
sub set_column_widths {
my $h = shift; # reference to hash table of speeds, keyed
+by datetime, then by database
my $databases = shift; # reference to hash of database names, wher
+e we will set the width values
my $names = shift; # ref to array of database names
# (so we don't need to call keys on $da
+tabases repeatedly)
for my $key (keys %$h){
for my $db (@$names){
my $l = length $h->{$key}{$db}; # get length of this item
+ in the hash table
# set column width to the
+ wider length
$databases->{$db} = $databases->{$db} > $l ? $databases->{
+$db} : $l;
}
}
for my $db (@$names){ # check the width of the database names the
+mselves too
my $l = length $db;
$databases->{$db} = $databases->{$db} > $l ? $databases->{$db}
+ : $l;
}
}
while(<DATA>){
next unless /\w/; # skip blank line
+s
my($datetime,$database,$speed) = (split)[1,2,3];
my $ddhhmm = substr $datetime,0,16; # substr works we
+ll here since the lengths are static
my $ddhh = substr $datetime,0,13; # this one doesn'
+t include the minutes
$h{$ddhh }{$database} += $speed; # add the speed t
+o this hour & database
$m{$ddhhmm}{$database} += $speed; # add the speed t
+o this minute & database
$db{$database} = 1; # save the databa
+se name
}
my @db = sort keys %db; # sort and save database names as array since
+we'll be looping through them many times
# HOUR SECTION START
# calculate column widths
set_column_widths(\%h, \%db, \@db);
# print out the per-hour stats
# starting with a header line
print "Frequency Hour:\ncollectionTime";
printf " %$db{$_}s", $_ for (@db); # print each database name as a h
+eader with dynamic width
print "\n"; # end of line
for my $key (sort keys %h){
print "$key "; # print the date/hou
+r key
printf " %$db{$_}s", $h{$key}{$_} for (@db); # print the value fo
+r each database that goes with this key
print "\n";
}
# HOUR SECTION END
# MINUTE SECTION START (using %m instead of %h)
# calculate column widths
set_column_widths(\%m, \%db, \@db);
# print out the per-minute stats
# starting with a header line
print "\nFrequency Minute:\n collectionTime";
printf " %$db{$_}s", $_ for (@db); # print each database name as a h
+eader with dynamic width
print "\n"; # end of line
for my $key (sort keys %m){
print $key; # print the date/hou
+r/minute key
printf " %$db{$_}s", $m{$key}{$_} for (@db); # print the value fo
+r each database that goes with this key
print "\n";
}
# MINUTE SECTION END
__DATA__
server01: 2015-06-01T12:40:03-04:00 DB101 10 MB/sec
server01: 2015-06-01T12:40:03-04:00 DB202 5 MB/sec
server01: 2015-06-01T12:40:03-04:00 ASM 2 MB/sec
server01: 2015-06-01T12:40:03-04:00 MYDB101 2 MB/sec
server01: 2015-06-01T12:40:03-04:00 MYDB202 5 MB/sec
server01: 2015-06-01T12:40:03-04:00 _OTHER_DB_ 30 MB/sec
server01: 2015-06-01T12:41:03-04:00 DB101 3 MB/sec
server01: 2015-06-01T12:41:03-04:00 DB202 4 MB/sec
server01: 2015-06-01T12:41:03-04:00 ASM 2 MB/sec
server01: 2015-06-01T12:41:03-04:00 MYDB101 9 MB/sec
server01: 2015-06-01T12:41:03-04:00 MYDB202 7 MB/sec
server01: 2015-06-01T12:41:03-04:00 _OTHER_DB_ 50 MB/sec
server02: 2015-06-01T12:40:03-04:00 DB101 90 MB/sec
server02: 2015-06-01T12:40:03-04:00 DB202 9 MB/sec
server02: 2015-06-01T12:40:03-04:00 ASM 2 MB/sec
server02: 2015-06-01T12:40:03-04:00 MYDB101 3 MB/sec
server02: 2015-06-01T12:40:03-04:00 MYDB202 1 MB/sec
server02: 2015-06-01T12:40:03-04:00 _OTHER_DB_ 90 MB/sec
server02: 2015-06-01T12:41:03-04:00 DB101 1 MB/sec
server02: 2015-06-01T12:41:03-04:00 DB202 4 MB/sec
server02: 2015-06-01T12:41:03-04:00 ASM 2 MB/sec
server02: 2015-06-01T12:41:03-04:00 MYDB101 7 MB/sec
server02: 2015-06-01T12:41:03-04:00 MYDB202 7 MB/sec
server02: 2015-06-01T12:41:03-04:00 _OTHER_DB_ 55 MB/sec
Aaron B.
Available for small or large Perl jobs and *nix system administration; see my home node.
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.