Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Help with Creating a Table from array ref I parsed in

by perlynewby (Scribe)
on May 12, 2022 at 06:57 UTC ( [id://11143808]=perlquestion: print w/replies, xml ) Need Help??

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

Hello Monks, I need help understanding how to format some text. I opted for an array since I do not know how to add multiple values to key in a hash. Clearly, I do not understand how to manipulate the array of elements to create a table equally align.

desired output, name of probe on the left side then all (x,y) coords and units on the right:

MM_STD_gate -61.771653 -45.472441 mils + -1569.000000 -1155.000000 micro +ns RFN_a -91.102362 68.307087 mils + -2314.000000 1735.000000 micro +ns RFN_b -62.165354 68.307087 mils + -1579.000000 1735.000000 micron +s RFN_c -51.417322 68.307087 mils + -1306.000000 1735.000000 micro +ns
#!/usr/bin/perl #use strict; use warnings; use Data::Dumper; my @records; my @col; my ($probe, $x_coord, $y_coord, $mils, $micron); my $infile= 'coord.txt'; open (DATA,'<',$infile) or die "could not open the $infile: $!"; while (<DATA>) { chomp; my @col = split " ", $_; push @records, \@col; } #print Dumper(\@col, @records); for (\@col){ print join("\t",$col{$records[0]}, $col{$records[1]}, $col{$record +s[2]}, $col{$records[3]}); } #this for loop doesn't work

DATA can be presented in unalign format

__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

Replies are listed 'Best First'.
Re: Help with Creating a Table from array ref I parsed in
by kcott (Archbishop) on May 12, 2022 at 10:49 UTC

    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

      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

      Thank you Ken, that a nicely done format, thanks

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

      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.

Re: Help with Creating a Table from array ref I parsed in
by choroba (Cardinal) on May 12, 2022 at 11:58 UTC
    Another approach (TIMTOWTDI): use Text::Table.

    #! /usr/bin/perl use warnings; use strict; use Text::Table; my $t = 'Text::Table'->new; while (<DATA>) { my @cols = split; $t->add(("") x (3 == @cols), @cols); } print $t;

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Help with Creating a Table from array ref I parsed in
by Tux (Canon) on May 12, 2022 at 07:01 UTC

    my @col = split " ", $_; => my @col = split m/\s+/ => $_;


    Enjoy, Have FUN! H.Merijn
Re: Help with Creating a Table from array ref I parsed in
by tybalt89 (Monsignor) on May 12, 2022 at 16:49 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11143808 use warnings; printf "%-13s%15.6f%15.6f %s\n" x 2, split, '', split ' ', <DATA> while <DATA>; __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

    Outputs:

    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

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (2)
As of 2024-04-26 05:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found