Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Dynamically build a table

by Deep_Plaid (Acolyte)
on Mar 11, 2014 at 17:44 UTC ( [id://1077875]=perlquestion: print w/replies, xml ) Need Help??

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

This question really has two parts: 1) Parse data into a data structure that can 2) be put into a table that will have a different number of rows depending on the data input.

I can't figure out what to use exactly to organize the data, and the answer may be contingent on how the second part is solved (building of the table). I know a hash won't work because I will have rows of data with "keys" that are duplicated, and that have multiple values. So I maybe I need a multidimensional array? Hopefully my code example below will make it more clear on what I am trying to do. I will be importing data from a file that has data structured as shown in the $file variable (<version number> <environment> <date>). I want to put this data in a table that is ordered by columns that are the environment, and rows that are the version number (the version number in the table corresponds to the module, so version 1.xx = module 1, version 2.xx = module 2, etc.). I am using text::table to build the table, but I realize that might not be the correct choice if I need to dynamically build rows.

Here is a code snippet:

use strict; use warnings; use text::table; use Data::Dumper; sub main { my $file = "1.00 InDev 01-Jun-2013 1.00 InTest 15-Jul-2013 1.00 InUAT 31-Jul-2013 1.00 InProd 15-Sep-2013 1.01 InDev 01-Jul-2013 2.00 InDev 01-Aug-2013 3.00 InDev 01-Sep-2013"; # This seems to be the only way to convert the scalar file to an array # where each line is an array element. There's probably a better way. my @array = split( /\n/, $file ); # This is simply a test to see what the array looks like print Dumper(@array); }; main(); # This next bit of code is not connected to the above because I don't # know the best way to build a data structure for the table. # Therefore I'm just creating a simple dummy array based on what a # single line of the table might look like. In addition, if there # are no matches with an (x,y) location in the table for a certain # row of data, I would like to fill it with the text "N/A", # hard coded as an illustration below. my @tarray = ( "1.0", "06-Mar-2014", "06-Mar-2014", "06-Mar-2014", "06-Mar-2014" +); my $tb = Text::Table->new( "Module", "Version", "InTest", "InUAT", "IN +Prod" ); $tb->load( [ "Module 1", $tarray[0], $tarray[1], $tarray[2], $tarray[3] ], [ "Module 2", "N/A", "N/A", "N/A", "N/A" ], [ "Module 3", "N/A", "N/A", "N/A", "N/A" ], ); print $tb;

Here's an example of the desired output based on the input of $file above:

Module Version InDev InTest InUAT INProd Module 1 1.00 01-Jun-2013 15-Jul-2013 31-Jul-2013 15-Sep-2013 Module 1 1.01 01-Jul-2013 N/A N/A N/A Module 2 2.00 01-Aug-2013 N/A N/A N/A Module 3 3.00 01-Sep-2013 N/A N/A N/A

I realize this is a rather complex, multi-part question, but I thought it would be most clear if entered as one question to understand all the parts. Any and all assistance would be greatly appreciated. Thank you.

Replies are listed 'Best First'.
Re: Dynamically build a table
by Cristoforo (Curate) on Mar 11, 2014 at 21:12 UTC
    Borrowing from 2teez's solution, the table could be loaded like this.

    Update A possible change of code to fit Deep_Plaid's followup question.

    #!/usr/bin/perl use strict; use warnings; use Text::Table; my $file = "1.00 InDev 01-Jun-2013 1.00 InTest 15-Jul-2013 1.00 InUAT 31-Jul-2013 1.00 InProd 15-Sep-2013 1.01 InDev 01-Jul-2013 2.00 InDev 01-Aug-2013 3.00 InDev 01-Sep-2013"; open my $fh, '<', \$file; my %line; while (<$fh>) { my ($ver, $status, $date) = split; $line{ $ver }{ $status } = $date; } my @status = qw/ InDev InTest InUAT InProd /; my @module = (undef, qw/ IXR Reports Three /); my $tb = Text::Table->new( "Module", "Version", @status ); for my $ver (sort keys %line) { my ($i) = $ver =~ /(\d+)/; $tb->load([ $module[$i], $ver, map $_ // 'N/A', @{$line{$ver}}{ @s +tatus } ]); } print $tb;
    Produces output:
    Module Version InDev InTest InUAT InProd IXR 1.00 01-Jun-2013 15-Jul-2013 31-Jul-2013 15-Sep-2013 IXR 1.01 01-Jul-2013 N/A N/A N/A Reports 2.00 01-Aug-2013 N/A N/A N/A Three 3.00 01-Sep-2013 N/A N/A N/A

      Thanks so much Cristoforo! This is great! I knew there had to be a fairly simple way to do this. I will need to step through this to really understand each piece, but I know I will learn a lot. There's only one minor thing missing. I will try to fix it myself, but any hints are appreciated. I need to correspond the module with the first digit of each version number (e.g., if version = 1.xx then "Module 1", if version = 2.xx then "Module 2", etc.). This is really a matching exercise because in real life the module names don't match the version numbers (only in my example). E.g., Module1 is really just a platform name such as IXR, Module 2 is Reports, etc.). Thanks again!

Re: Dynamically build a table
by 2teez (Vicar) on Mar 11, 2014 at 20:24 UTC

    Hi Deep Plaid,

    .. I know a hash won't work because I will have rows of data with "keys" that are duplicated, and that have multiple values..
    Really? Except I don't understand what you wanted. Am sure, hash would work perfectly, using the dataset you presented like so:

    use warnings; use strict; use Data::Dumper; my $file = "1.00 InDev 01-Jun-2013 1.00 InTest 15-Jul-2013 1.00 InUAT 31-Jul-2013 1.00 InProd 15-Sep-2013 1.01 InDev 01-Jul-2013 2.00 InDev 01-Aug-2013 3.00 InDev 01-Sep-2013"; my %line; for ( split /\n/, $file ) { my @datas = split; push @{ $line{ $datas[0] }{ $datas[1] } }, $datas[2]; } { $Data::Dumper::Sortkeys = 1; $Data::Dumper::Indent = 3; print Dumper \%line; }
    Which gives the following output:
    $VAR1 = { '1.00' => { 'InDev' => [ #0 '01-Jun-2013' ], 'InProd' => [ #0 '15-Sep-2013' ], 'InTest' => [ #0 '15-Jul-2013' ], 'InUAT' => [ #0 '31-Jul-2013' ] }, '1.01' => { 'InDev' => [ #0 '01-Jul-2013' ] }, '2.00' => { 'InDev' => [ #0 '01-Aug-2013' ] }, '3.00' => { 'InDev' => [ #0 '01-Sep-2013' ] } };
    So, one out of your questions, is actually done for you, the second one you can easily do. :)!
    *I hope the above helps. You can also take a look at perldsc

    * Update

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me

      Hi - sorry for not getting back to you sooner. I got caught up in corporate manure. Thanks for taking a look at this and for your advice. Another user added to what you wrote and I will try that out. Thanks again!

Re: Dynamically build a table
by Laurent_R (Canon) on Mar 11, 2014 at 18:21 UTC

    This may be what you want:

    use strict; use warnings; use Data::Dumper; my $file = "1.00 InDev 01-Jun-2013 1.00 InTest 15-Jul-2013 1.00 InUAT 31-Jul-2013 1.00 InProd 15-Sep-2013 1.01 InDev 01-Jul-2013 2.00 InDev 01-Aug-2013 3.00 InDev 01-Sep-2013"; my @file_content = split( /\n/, $file ); my @full_data; for my $line (@file_content) { my @fields = split /\s+/, $line; push @full_data, \@fields; } print Dumper \@full_data;
    which prints the data this way:
    $ perl table.pl $VAR1 = [ [ '1.00', 'InDev', '01-Jun-2013' ], [ '1.00', 'InTest', '15-Jul-2013' ], [ '1.00', 'InUAT', '31-Jul-2013' ], [ '1.00', 'InProd', '15-Sep-2013' ], [ '1.01', 'InDev', '01-Jul-2013' ], [ '2.00', 'InDev', '01-Aug-2013' ], [ '3.00', 'InDev', '01-Sep-2013' ] ];

    The four important lines:

    for my $line (@file_content) { my @fields = split /\s+/, $line; push @full_data, \@fields; }
    could be made much more concise:
    push @full_data, [split] for @file_content;
    but I preferred to keep a step by step approach to make understanding easier.

    Update:: when I posted the above, Deep_Plaid had not updated the original post with the addition of the desired output. As I said in another post above, it may not be necessary to build this AoA to arrived at the desired output. The solution might be much simpler.

      Hi LR. Yes, the array name isn't the best - this isn't my original code but a dumbed down version I put together as an example. Yours is a much better name choice. Your AoA example looks like what I want - thanks for posting. Do you have any ideas on the second part - how to dynamically fill a table? Is the table::text module even capable of this? I added an output example to my original post for clarity. Thanks!

        Hi Deep_Plaid, What do you mean by "how to dynamically fill a table"? Do you just want display (print) the data in tabular format? Is it something else?

        Hi, is this what you are looking for:

        use strict; use warnings; use Data::Dumper; my $file = "1.00 InDev 01-Jun-2013 1.00 InTest 15-Jul-2013 1.00 InUAT 31-Jul-2013 1.00 InProd 15-Sep-2013 1.01 InDev 01-Jul-2013 2.00 InDev 01-Aug-2013 3.00 InDev 01-Sep-2013"; my @file_content = split( /\n/, $file ); my @full_data; push @full_data, [split] for @file_content; for my $line_ref (@full_data) { print "$_\t" for @$line_ref; print "\n"; }
        This is the output:
        $ perl table.pl 1.00 InDev 01-Jun-2013 1.00 InTest 15-Jul-2013 1.00 InUAT 31-Jul-2013 1.00 InProd 15-Sep-2013 1.01 InDev 01-Jul-2013 2.00 InDev 01-Aug-2013 3.00 InDev 01-Sep-2013

        Update Mar 11, 19:45 UTC: Looking again at the OP and at another answer you made which I had not seen, I think I now understand what you are trying to do (and the above is not what you need). You're basically wanting to do a form of transposition of the data, where, in part, what you have in row form should not appear in columns, and vice-versa, although it is not a simple matrix transposition. I don't have time right now (I'm gonna have dinner with my family right now), but I think I can come back to it in a couple of hours unless someone else does it before me.

Re: Dynamically build a table
by choroba (Cardinal) on Mar 11, 2014 at 18:01 UTC
    Can you please include the expected output? I feel lost.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      Hi Choroba, I added an example of the desired output. Thanks! DP

        Well, I had not seen your desired output when I posted my post with example code below, but since there is no relation between the input that you had shown and the output that you are now showing, could you please provide the type of input from which you want to get the desired output. It is not obvious that you really need to store the data into a table to get the output you want if the input look like what I can, think of.
Re: Dynamically build a table
by Laurent_R (Canon) on Mar 11, 2014 at 18:01 UTC
    The most obvious data structure to store you data is an array of arrays (AoA), but depending on how you want to use your data afterwards, you may want to use an array of hashes or even a hash of arrays or a hash of hashes. I would advise against using a variable name such as @array, it is obvious from the @ sigil that it is an array, a variable name indicating the content of the array is more useful, for example @content_of_file or even something more specific.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2024-04-19 11:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found