Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

How to print after using getline_hr (Text::CSV_XS)?

by vagabonding electron (Curate)
on Nov 09, 2011 at 10:19 UTC ( [id://937023]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,
after some experience in parsing the csv files with split & join I try to use the modul Text::CSV_XS now. I get a problem while trying to print the file if I use getline_hr.
A bit of case history. I need to modify some values in some columns. These columns have always the same name (header) but the are at the different positions in the different csv files. I must therefore access these columns by names.
The first script below works - because I take every column by name and combine them later. This cannot be a solution because as said the required columns can be at different positions in the different files and the number of columns can be different too.
use strict; use warnings; use 5.010; use Text::CSV_XS; my $csv = Text::CSV_XS->new( { binary => 1, sep_char=>';' } ) or die " +Cannot use CSV: ".Text::CSV->error_diag(); my @fields = ('ID', 'NAME','INDICATOR','VALUE','CI_LOW', 'CI_HIGH'); $csv->print( \*STDOUT, \@fields ); say ''; $csv->column_names( $csv->getline( *DATA ) ); while ( my $hr = $csv->getline_hr( *DATA ) ) { my @nr = @{$hr}{'ID'}; s/12/Sun/g for @nr; s/17/Moon/g for @nr; my @name = @{$hr}{'NAME'}; my @ind = @{$hr}{'INDICATOR'}; my @val = @{$hr}{'VALUE'}; my @low = @{$hr}{'CI_LOW'}; my @high = @{$hr}{'CI_HIGH'}; if ($csv->combine ( @nr, @name, @ind, @val, @low, @high)) { print $csv->string, "\n"; } else { print "combine () failed on argument: ", $csv->error_input, "\n"; } } __DATA__ ID;NAME;INDICATOR;VALUE;CI_LOW;CI_HIGH 12;Name1;01;95;89;100 12;Name1;02;75;50;92 12;Name1;03;89;82;96 12;Name2;01;99;98;100 12;Name2;02;95;90;100 12;Name2;03;22;12;32 17;Name1;01;93;83;95 17;Name1;02;78;62;96 17;Name1;03;37;17;57
If I try it the other way the script shows an error:
use strict; use warnings; use 5.010; use Text::CSV_XS; my $csv = Text::CSV_XS->new( { binary => 1, sep_char=>';' } ) or die " +Cannot use CSV: ".Text::CSV->error_diag(); my @fields = ('ID', 'NAME','INDICATOR','VALUE','CI_LOW', 'CI_HIGH'); $csv->print( \*STDOUT, \@fields ); say ''; # $csv->column_names( $csv->getline( *DATA ) ); while ( my $hr = $csv->getline_hr( *DATA ) ) { # my @nr = @{$hr}{'ID'}; s/12/Sun/g for @{$hr}{'ID'}; s/17/Moon/g for @{$hr}{'ID'}; $csv->print (*STDOUT, $hr) or $csv->error_diag; } __DATA__ ID;NAME;INDICATOR;VALUE;CI_LOW;CI_HIGH 12;Name1;01;95;89;100 12;Name1;02;75;50;92 12;Name1;03;89;82;96 12;Name2;01;99;98;100 12;Name2;02;95;90;100 12;Name2;03;22;12;32 17;Name1;01;93;83;95 17;Name1;02;78;62;96 17;Name1;03;37;17;57
The error message is: Expected fields to be an array ref at ...
It is correct since by using geline_hr it is hash reference not array reference.
I tried it the way as [%$hr] - but no success.
Your help is very much appreciated!
Thanks in advance.
VE

Replies are listed 'Best First'.
Re: How to print after using getline_hr (Text::CSV_XS)?
by Tux (Canon) on Nov 09, 2011 at 11:46 UTC

    The print method expects an array reference, not a hash reference:

    use v5.12; use warnings; use Text::CSV_XS; my $csv = Text::CSV_XS->new ({ binary => 1, sep_char => ";", eol => $/, auto_diag => 1, }); my @fields = qw( ID NAME INDICATOR VALUE CI_LOW CI_HIGH ); $csv->print (*STDOUT, \@fields); say ''; my @columns = @{$csv->getline (*DATA)}; $csv->column_names (@columns); while (my $hr = $csv->getline_hr (*DATA)) { # my @nr = @{$hr}{'ID'}; $hr->{ID} =~ s/12/Sun/g; $hr->{ID} =~ s/17/Moon/g; $csv->print (*STDOUT, [ map { $hr->{$_} } @columns ]); }

    Much easier to read and much faster too would be to use bind_columns

    my @fields = qw( ID NAME INDICATOR VALUE CI_LOW CI_HIGH ); $csv->print (*STDOUT, \@fields); my @columns = @{$csv->getline (*DATA)}; my %rec; $csv->bind_columns (\@rec{@columns}); while ($csv->getline (*DATA)) { $rec{ID} =~ s/12/Sun/g; $rec{ID} =~ s/17/Moon/g; $csv->print (*STDOUT, [ @rec{@columns} ]); }

    update: I stripped the data sections from the code as they are identical to those in the OP and reduced the second example to show only the changed part.


    Enjoy, Have FUN! H.Merijn
      Thank you very much for your help Tux!
      I changed now the printing of the header too so that I do not need to hardcode the fields anymore:
      my @columns = @{$csv->getline (*DATA)}; $csv->print (*STDOUT, \@columns);
      Thanks again!
      VE
Re: How to print after using getline_hr (Text::CSV_XS)?
by jethro (Monsignor) on Nov 09, 2011 at 11:55 UTC

    Your first script uses a very convoluted way to construct the output. You use arrays to store simple scalar variables. You use loops where only a single value is concerned. You process the CSV file line by line, but somehow seem to think that you process a whole column at a time.

    #loop from your first script: while ( my $hr = $csv->getline_hr( *DATA ) ) { my $nr = $hr->{'ID'}; $nr=~ s/12/Sun/; # neither the g nor the loop are necessary $nr=~ s/17/Moon/; my $name = $hr->{'NAME'}; # only one value in column NAME of this +line my $ind = $hr->{'INDICATOR'}; my $val = $hr->{'VALUE'}; my $low = $hr->{'CI_LOW'}; my $high = $hr->{'CI_HIGH'}; if ($csv->combine ( $nr, $name, $ind, $val, $low, $high)) { print $csv->string, "\n"; } else { print "combine () failed on argument: ", $csv->error_input, "\n"; } }

    Likewise "$hr->{'ID'}=~ s/12/Sun/;" could be used in your second script and the print adapted like Tux suggested

      Thank you very much jethro I seem to understand it better now (well, at least I hope so :-))
      VE

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2024-04-25 23:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found