in reply to Re: Creating CSV file
in thread Creating CSV file

OK I`ll try: During processing in previous part of a program I received following three arrays:

@ports=qw(portname portID port802); @val1= qw(ON ON OFF); @val2=qw(EX EX NEX);

Output i received:

portname;portID;port802;

What i need is to print in CSV something like this:

ports;portname;ON;EX; ports;portID;ON;EX; ports;port802;OFF;NEX;

Hope this will clarify a little bit, just don`t know how to get this using Text::CSV module.

Replies are listed 'Best First'.
Re^3: Creating CSV file
by GrandFather (Saint) on Jul 31, 2010 at 23:00 UTC

    Here data structure is the key. It looks like you are using three arrays in parallel to manage data records. Instead you should be using an array of records - each record being either a hash or an array as you think most appropriate. Consider:

    #!/usr/bin/perl use strict; use warnings; use Text::CSV; my @ports = qw(portname portID port802); my @val1 = qw(ON ON OFF); my @val2 = qw(EX EX NEX); my @records; while (@ports) { push @records, [shift @ports, shift @val1, shift @val2]; } my $csv = Text::CSV->new ({sep_char => ';'}); for my $record (@records) { if ($csv->combine (@$record)) { print $csv->string, "\n"; } else { print "combine () failed on argument: ", $csv->error_input, "\ +n"; } }

    Prints:

    portname;ON;EX portID;ON;EX port802;OFF;NEX

    Note that much of the rest of your program would likely benefit from using record oriented storage as illustrated rather than scattering related data across disparate arrays as indicated in your sample.

    True laziness is hard work

      Or, much more consice:

      #!/usr/bin/perl use strict; use warnings; use Text::CSV; my @ports = qw(portname portID port802); my @val1 = qw(ON ON OFF); my @val2 = qw(EX EX NEX); my $csv = Text::CSV->new ({sep_char => ';', eol => "\n", auto_diag => +1 }); for (@ports) { $csv->print (*STDOUT, [ $_, shift @val1, shift @val2 ]); }

      Enjoy, Have FUN! H.Merijn

        Concise was not the point. Adopting an appropriate data structure was. Note that the meat of my reply however boils down to:

        $csv->print (*STDOUT, $_) for @records;

        where your 'concise' equivalent was:

        for (@ports) { $csv->print (*STDOUT, [ $_, shift @val1, shift @val2 ]); }

        which after all is part of why an appropriate data structure is a 'good thing'™ ;-)

        True laziness is hard work
Re^3: Creating CSV file
by graff (Chancellor) on Jul 31, 2010 at 22:56 UTC
    I'm not sure that you need Text::CSV to get the output you want. The following subroutine will do what you describe:
    sub combine_rows { my ( $label, @arrays ) = @_; my @joined; for my $i ( 0 .. $#{$arrays[0]} ) { # assumes that all arrays are + the same size push @joined, join( ';', $label, map { $arrays[$_][$i] } 0 .. +$#arrays ); } return \@joined; }
    Here's how you would use the subroutine:
    @ports = qw( portname portID port802 ); @val1 = qw( ON ON OFF ); @val2 = qw( EX EX NEX ); my $joined = combine_rows( "ports", \@ports, \@val1, \@val2 ); print "$_\n" for ( @$joined );
    Now, it would be a little more complicated if your input array values can contain the semi-colon character as data. In that case, you need to escape or quote the field values in the subroutine. (That's the sort of thing Text::CSV would do for you if you used it properly, but it's not that complicated to do on your own.)

    The reason I use "map" in the subroutine is to allow it to join any number of parallel arrays (not just exactly 3, as in your "@ports, @val1, @val2" example). If you can't figure out what "map" is doing there, it's just a nested loop to get the $i'th value of each input array.