Home > Software design >  How to show data values vertically instead of horizontal way
How to show data values vertically instead of horizontal way

Time:08-13

I have written a script which will fetch data from db with certain condition and store it in the hash. I need these stored hash content to show in the tabular format. Right now I have all the data which should be shown in the table is there in the hash called %hash.

I need to display the content of the hash in console in tabular format.

Below is my script:

#!/usr/bin/perl

use strict; use warnings;
use Data::Dumper;

my %hash = (
  '2022-08-04' => {
      'Method 1' => {
            'Count' => 50,
            'Size' => '10 MB'
      },
      'Method 2' => {
            'Count' => 40,
            'Size' => '5 MB'
      }
  },
  '2022-08-05' => {
      'Method 1' => {
            'Count' => 30,
            'Size' => '3 MB'
      },
      'Method 2' => {
            'Size' => '50 MB',
            'Count' => '100'
      }
  }
);

my @cols;
my @keys = ('Method 1', 'Method 2');

foreach my $date (sort keys %hash){
    foreach my $method (@keys) {
    push (@recs, { date => $date, size => $hash{$date}{$method}{'Size'}, count => $hash{$date}{$method}{'Count'}});
    }
}

print "cols:\n".Dumper(\@cols)."\n";

my $line1 = '| ' . join(' | ', map { sprintf "%-*s", 20, $_->{date} } @cols) . ' |';
my $line2 = '| ' . join(' | ', map { sprintf "%-*s", 20, $_->{size} } @cols) . ' |';
my $line3 = '| ' . join(' | ', map { sprintf "%-*s", 20, $_->{count} } @cols) . ' |';

print $line1."\n";
print $line2."\n";
print $line3."\n";

Current Result:

| 2022-08-04           | 2022-08-04           | 2022-08-05           | 2022-08-05           |
| 10 MB                | 5 MB                 | 3 MB                 | 50 MB                |
| 50                   | 40                   | 30                   | 100                  |

Expected Result:

-----------------------------------------------------------------------------------
| Date          | Method 1 Size | Method 1 Count | Method 2 Size | Method 2 Count |
-----------------------------------------------------------------------------------
| 2022-08-04    | 10 MB         | 50             | 5 MB          | 40             |
| 2022-08-05    | 3 MB          | 30             | 50 MB         | 100            |
-----------------------------------------------------------------------------------

Since the data I'm pushing into @cols, each of the cols array element contains a hash with date, size and count. And while printing them it shows in horizontal way instead of showing it in vertically.

Also, sometimes there wouldn't be Method 2 hash values. In that scenario I need to display content of Method 1 hash values in table alone. How to achieve it in dynamic way?

CodePudding user response:

You have an outer loop over the fields (the copy and pasted lines), and an inner loop over the flattened records.

But the desired output shows a loop over the dates wrapping a loop over the methods wrapping a loop over the fields.

Structure illustrated

(Click to enlarge to get a better view.)

{
   printf "| %-*s | ", 20, "Date";

   for my $method ( "Method 1", "Method 2" ) {
      printf "%-*s | %-*s |",
         20, "$method Size",
         20, "$method Count",
   }

   printf "\n";
}

for my $date ( sort keys( %hash ) ) {
   my $by_method = $hash{ $date };

   printf "| %-*s | ", $date;

   for my $method ( "Method 1", "Method 2" ) {
      my $rec = $by_method->{ $method };

      printf "%-*s | %-*s |",
         20, $rec->{ size  },
         20, $rec->{ count };
   }

   printf "\n";
}

There's a lot of magic numbers in there.

my @methods = ( "Method 1", "Method 2" );

my $date_size = 20;

my @field_defs = (
   { name => "Size",  size => 20 },
   { name => "Count", size => 20 },
);

{
   printf "| %-*s | ", $date_size, "Date";

   for my $method ( @methods ) {
      for my $field_def ( @field_defs ) {
         printf "%-*s |", $field_def->{ size }, $field_def->{ name };
      }
   }

   printf "\n";
}

for my $date ( sort keys( %hash ) ) {
   my $by_method = $hash{ $date };

   printf "| %-*s | ", $date_size, $date;

   for my $method ( @methods ) {
      my $rec = $by_method->{ $method };

      for my $field_def ( @field_defs ) {
         printf "%-*s |", $field_def->{ size }, $rec->{ $field_def->{ name } };
      }
   }

   printf "\n";
}

The next step might be to calculate the minimum sizes instead of using 20.

CodePudding user response:

Added as a new answer, because i think both styles are worth. Like i said in my other answer. Do the transformation of data before-hand. Then create your table. Here is another way of data-transformation by flattening the inner-hash first.

#!/usr/bin/env perl
use strict;
use warnings;
use v5.32;
use Data::Printer;
use Text::Table;
use List::Util qw(uniqstr);

my %hash = (
  '2022-08-04' => {
      'Method 1' => {
            'Count' => 50,
            'Size' => '10 MB'
      },
      'Method 2' => {
            'Count' => 40,
            'Size' => '5 MB'
      }
  },
  '2022-08-05' => {
      'Method 1' => {
            'Count' => 30,
            'Size' => '3 MB'
      },
      'Method 2' => {
            'Size' => '50 MB',
            'Count' => '100'
      }
  }
);

# Transform %hash by flattening the inner hashes
my            
  • Related