in reply to Help with Creating a Table from array ref I parsed in

G'day perlynewby,

"... I do not know how to add multiple values to key in a hash."

See "perldsc - Perl Data Structures Cookbook". There are links at the end to more detailed information.

"Clearly, I do not understand how to manipulate the array of elements to create a table equally align."

There are a variety of options. In the example code below, I've used formats — see sprintf for a full description of formatting options.

Update (disambiguation): "formats" (used in the previous paragraph) does not refer to "perlform - Perl formats".

"open (DATA,'<',$infile) ..."

Use lexical filehandles instead of package variables — see open. In this case, DATA is a poor choice; not only because it is a package variable but also because it's special — see "perldata: Special Literals".

"... or die "could not open the $infile: $!";"

Consider using the autodie pragma. It saves you having to type the "or die ..." code and is far less error-prone.

Here's an example showing the use of formatting:

#!/usr/bin/env perl use strict; use warnings; my $fmt = "%-11s % 12.6f % 12.6f %s\n% 24.6f % 12.6f %s\n"; while (<DATA>) { my @cols = split; $_ = <DATA>; push @cols, split; printf $fmt, @cols; } __DATA__ MM_STD_gate -61.771653 -45.472441 mils -1569.000000 -1155.000000 microns RFN_a -91.102362 68.307087 mils -2314.000000 1735.000000 microns RFN_b -62.165354 68.307087 mils -1579.000000 1735.000000 microns RFN_c -51.417322 68.307087 mils -1306.000000 1735.000000 + microns

Here's the output:

MM_STD_gate -61.771653 -45.472441 mils -1569.000000 -1155.000000 microns RFN_a -91.102362 68.307087 mils -2314.000000 1735.000000 microns RFN_b -62.165354 68.307087 mils -1579.000000 1735.000000 microns RFN_c -51.417322 68.307087 mils -1306.000000 1735.000000 microns

— Ken

Replies are listed 'Best First'.
Re^2: Help with Creating a Table from array ref I parsed in
by hippo (Archbishop) on May 12, 2022 at 11:30 UTC
    I've used formats

    <crocodile_dundee>That's not a format. This is a format!</crocodile_dundee>

    #!/usr/bin/env perl use strict; use warnings; my @fields; format STDOUT = @<<<<<<<<<< @####.###### @####.###### @<<<<<< @fields . while (my $line = <DATA>) { chomp $line; @fields = split /\s+/, $line, 4; unshift @fields, ' ' if 4 > @fields; write; } __DATA__ MM_STD_gate -61.771653 -45.472441 mils -1569.000000 -1155.000000 microns RFN_a -91.102362 68.307087 mils + -2314.000000 1735.000000 microns RFN_b -62.165354 68.307087 mils + -1579.000000 1735.000000 microns RFN_c -51.417322 68.307087 mils + -1306.000000 1735.000000 + microns

    TIMTOWTDI.


    🦛

      ++ Well wielded. :-)

      — Ken

      Thank you all! These sample codes really help me to think about how to manipulate arrays to create new formats.

      Hi TIMTOWTDI,

      I am a little lost trying to figure out how your script works.

      I read Perl #sprintf FORMAT, LIST and could not understand how your "format STDOUT" works. Can you explain it? The Perldocs I read do not have the format you presented here for your script

      Say, if I were to "left-justify" each column, how can I add the "-" to your "format STDOUT" statement and be left-justified? Also, how to manipulate the spacing in between columns?

      I's like the table to look like this below, perhaps, I can understand your format statement works if I see it...maybe?

      MM_STD_gate 152.401574 -22.047244 mils 3871.000000 -560.000000 microns RFN_a 337.244094 0.787402 mils 8566.000000 20.000000 microns RFN_b 366.181102 0.787402 mils 9301.000000 20.000000 microns RFN_c 376.929133 0.787402 mils 9574.000000 20.000000 microns RFLR 366.181102 -10.6299921 mils 9301.000000 -270.000000 microns MM_STD2_gate 259.488188 -22.047244 mils 6591.000000 -560.000000 microns RBASE_right_2 -366.181102 -33.464566 mils -9301.000000 -850.000000 microns

      This is your code below with the edit to allow a longer string for the 1st column

      #!/usr/bin/env perl use strict; use warnings; my @fields; # For the longer 1st Column name, I added some more spaces so it won't + concat the name. NOt sure how the left-justify can be done with this + format STDOUT statement? format STDOUT = @<<<<<<<<<<<< -@#####.###### @####.###### @<<<<<< @fields . while (my $line = <DATA>) { chomp $line; @fields = split /\s+/, $line, 4; unshift @fields, ' ' if 4 > @fields; write; } # All four columns should be left-justified and should line up whether + it is a positive or negative number. __DATA__ MM_STD_gate -61.771653 -45.472441 mils -1569.000000 -1155.000000 microns RFN_a -91.102362 68.307087 mils + -2314.000000 1735.000000 microns RFN_b -62.165354 68.307087 mils + -1579.000000 1735.000000 microns RFN_c -51.417322 68.307087 mils + -1306.000000 1735.000000 + microns MM_STD2_gate 259.488188 -22.047244 mils 6591.000000 -560.000000 microns RBASE_right_2 366.181102 -33.464566 mils 9301.000000 -850.000000 microns
        Hi TIMTOWTDI

        Hi perlynewby. TIMTOWTDI is an abbreviation: in this context it was there to highlight that the method I showed was different to that from kcott. I'm hippo.

        I read Perl #sprintf FORMAT, LIST and could not understand how your "format STDOUT" works. Can you explain it? The Perldocs I read do not have the format you presented here for your script

        You have been reading different docs. Please see the docs for format which I linked to previously and which are entirely unrelated to sprintf. Hopefully they will make it all clear. Give it a try once you have read through those and if you are still stuck please ask further.


        🦛

        Here is an earlier reply I did using format with aligned columns using sprintf: https://www.perlmonks.org/?node_id=11136290

        sample output from that thread:
        Package Coverage + Line Method Branch ------------------------------------------------------------------ +------------------------------------------------------- | com.gandu.base.alltests | 90% +| 91% (20/22) | 50% (1/2) | 0% (0/0) | | com.gandu.base.commons | 47% +| 43% (579/1336) | 34% (73/213) | 41% (111/270) | | com.gandu.base.commons.status | 27% +| 27% (103/388) | 46% (25/54) | 8% (5/60) | | com.gandu.base.commons.test | 94% +| 84% (62/74) | 85% (11/13) | 100% (6/6) | | com.gandu.base.commons.validchecker | 90% +| 90% (9/10) | 50% (1/2) | 0% (0/0) | | com.gandu.base.commons.validchecker.arghandler | 12% +| 8% (115/1404) | 8% (8/99) | 0% (0/350) | | com.gandu.base.commons.validchecker.centralarea | 26% +| 37% (111/297) | 65% (17/26) | 56% (47/84) | | com.gandu.base.commons.validchecker.testtag | 85% +| 82% (613/748) | 82% (93/114) | 53% (90/170) | | com.gandu.base.consoleindependentcommons | 91% +| 91% (79/87) | 82% (14/17) | 90% (18/20) | ------------------------------------------------------------------ +-------------------------------------------------------
        Fun stuff!
Re^2: Help with Creating a Table from array ref I parsed in
by perlynewby (Scribe) on May 13, 2022 at 03:04 UTC

    Thank you Ken, that a nicely done format, thanks

    I am reading about printf FORMAT, LIST makes this easier when passing an array

Re^2: Help with Creating a Table from array ref I parsed in
by perlynewby (Scribe) on May 16, 2022 at 05:04 UTC

    Hello Ken

    I've read printf FORMAT LIST and edited your program to learn how it behaves; however, I do not understand it completely, can you explain some things I am still doubtful?

    So, I edited your $fmt statement and tried to left-adjust and play with the spaces in between columns; however, I cannot understand the spaces between the 3rd and 4th columns.

     my $fmt = "%-13s % 24f %-24f %-4s\n %38.6f %14f %-4s\n";

    the output of some of the 2nd columns seems to be left-justified except when the number is negative. How can that negative be moved in one more space so everything is left-justified?

    And, the spacing between the 3rd and 4th columns are not clear to me yet? I know that Between the % and the format letter, some attributes control the interpretation, Can you explain how these spaces are moved in the 3rd and 4th fields?

    MM_STD_gate 152.401574 -22.047244 mils 3871.000000 -560.000000 microns RFN_a 337.244094 0.787402 mils 8566.000000 20.000000 microns RFN_b 366.181102 0.787402 mils 9301.000000 20.000000 microns RFN_c 376.929133 0.787402 mils 9574.000000 20.000000 microns MM_STD2_gate -259.488188 -22.047244 mils -6591.000000 -560.000000 microns RBASE_right_2 -366.181102 -33.464566 mils -9301.000000 -850.000000 microns

    I'll like to see all columns left-justified (the negative number is also left-justified with the number below or above) and little bigger spaces in between. Note, that there can only be 6 decimal numbers in columns and upto 13-character string name is allowed.

    MM_STD_gate 152.401574 -22.047244 mils 3871.000000 -560.000000 microns RFN_a 337.244094 0.787402 mils 8566.000000 20.000000 microns RFN_b 366.181102 0.787402 mils 9301.000000 20.000000 microns RFN_c 376.929133 0.787402 mils 9574.000000 20.000000 microns MM_STD2_gate -259.488188 -22.047244 mils -6591.000000 -560.000000 microns RBASE_right_2 -366.181102 -33.464566 mils -9301.000000 -850.000000 microns

    thanks for the explanations!

      perlnewby:

      In your format specifier, you have '%24f' for the third column in the first line, but '%14f' for the matching column in the second line which is why the string 'mils' is pushed so far to the right. It's saying "the third column is a float and should fill 24 columns", so by changing it to '%14f' to match the second line, you should get the fourth column on both lines to align.

      What I frequently do in cases like this is to first break a multiline format at the line break:

      my $fmt = "%-13s % 24f %-24f %-4s\n" . " %38.6f %-14f %-4s\n";

      And then add some 'fake' fields so that I can use the same formatting on both lines, and in the print statement, just insert '' for the arguments matching the fake fields:

      my $fmt = "%-13s % 24f %-14f %-4s\n" . "%-13s %24.6f %-14f %-4s\n"; . . . . printf $fmt, 'MM_STD_gate', 152.401574, -22.047244, 'mils', '', 3871.0, -560.0, 'microns';

      When you're doing things that aren't time critical, I find it useful to do something like this to make adjusting the format easier and paying a slight loss of inefficiency.

      Note: The formats for the printf() family of functions are pretty convenient, but have some limitations when you're trying to make text reports. Specifically, controlling field widths, aligning values on decimal points, etc., can be difficult. In fact, your format with the %-4s at the end is blowing past your formatted column width for the 'microns' field. Not important, as it's the last field on the line, but if you added another column to the right, you'd discover it fairly soon!

      A lesser-used output formatting technique when you're trying to reports on monospaced devices (like the console or a line printer) is the 'named formats' method of formatting text. (You can read the details about it with the 'perldoc perlform' command.) It's not something you necessarily want to use often, but you should read about it once or twice so you can be aware of it in the event that you want to use it in the future. It's kind of a throwback to the way reports are frequently done in COBOL on mainframes. One advantage of it is that it can handle top of form and bottom of form for printouts, and it's visually easy to edit. It also doesn't blow out the column widths: instead of breaking the report format, it fills fields that are too wide with '#####' instead (for numbers) or it just truncates the value (for text).

      However, now that we're in a more modern era where monospaced output isn't nearly so common and online reports are easier to deal with (and more environmentally friendly that printing a ream of paper for a report you'll use twice and then discard), it would be better to read about Template::Toolkit where you can generate *many* types of reports, flexibly.

      I was still waking up with my morning coffee, so I took a few minutes and put together a quickie demo of the first two techniques below. (I didn't demo the third technique because I don't have Template::Toolkit installed on this machine (and don't really use it much anyway.)

      Roboticus@Waubli ~ $ cat t.pl use strict; use warnings; # The data my @recs = ( [ 'MM_MM_STD_gate', 152.401574, -22.047244, 'mils', 3871.0, -560.0, 'microns' ], [ 'RFN_a', 337.244094, 0.787402, 'mils', 8566.0, 20.0, 'microns' ], [ 'RFN_b', 366.181102, 0.787402, 'mils', 9301.0, 20.0, 'microns' ], [ 'RFN_c', 376.929133, 0.787402, 'mils', 9574.0, 20.0, 'microns' ], [ 'MM_STD2_gate', -259.488188, -22.047244, 'mils', -6591.0, -560.0, 'microns' ], [ 'RBASE_right_2', -366.181102, -33.464566, 'mils', -9301.0, -850.0, 'microns' ], ); print "\n------ Report1: (printf) ------\n\n"; # I made it easy on myself by *not* using the fake field here so I cou +ld # just print the data by expanding the data in-line in the printf stat +ement. my $fmt = "%-13s % 24f %-14f %-4s\n" . " %24.6f %-14f %-4s\n"; for my $ar (@recs) { printf $fmt, @$ar; } print "\n\n------ Report 2 (Named formats) ------\n\n"; # The format statement embeds the variables to map to the fields inlin +e, so # the variable must already be declared my $ar; # Define the format visually, along with the variables to stuff into t +hem format DETAIL = @<<<<<<<<<<<< @####.##### @###.###### @<<<<<<<<< $ar->[0], $ar->[1], $ar->[2], $ar->[3], @####.##### @###.###### @<<<<<<<<< $ar->[4], $ar->[5], $ar->[6]; . # Specify the format type to use for each write statement (you can hav +e # multiple formats and switch between them as needed). select(STDOUT); $~ = "DETAIL"; # Print the report for $ar (@recs) { write; } Roboticus@Waubli ~ $ perl t.pl ------ Report1: (printf) ------ MM_MM_STD_gate 152.401574 -22.047244 mils 3871.000000 -560.000000 microns RFN_a 337.244094 0.787402 mils 8566.000000 20.000000 microns RFN_b 366.181102 0.787402 mils 9301.000000 20.000000 microns RFN_c 376.929133 0.787402 mils 9574.000000 20.000000 microns MM_STD2_gate -259.488188 -22.047244 mils -6591.000000 -560.000000 microns RBASE_right_2 -366.181102 -33.464566 mils -9301.000000 -850.000000 microns ------ Report 2 (Named formats) ------ MM_MM_STD_gat 152.40157 -22.047244 mils 3871.00000 -560.000000 microns RFN_a 337.24409 0.787402 mils 8566.00000 20.000000 microns RFN_b 366.18110 0.787402 mils 9301.00000 20.000000 microns RFN_c 376.92913 0.787402 mils 9574.00000 20.000000 microns MM_STD2_gate -259.48819 -22.047244 mils -6591.00000 -560.000000 microns RBASE_right_2 -366.18110 -33.464566 mils -9301.00000 -850.000000 microns

      ...roboticus

      When your only tool is a hammer, all problems look like your thumb.