more useful options PerlMonks

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

by vagabonding electron (Chaplain)
 on Nov 09, 2011 at 10:19 UTC 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 [download] 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 [download] 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!
VE

Replies are listed 'Best First'.
Re: How to print after using getline_hr (Text::CSV_XS)?
by Tux (Abbot) 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 ]); } [download] 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} ]);
}
[download]

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);
[download]
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"; } } [download] 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

Create A New User
Node Status?
node history
Node Type: perlquestion [id://937023]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
 [thezip]: Is there an analogy for '&' (ie. run commandline process in background) for Windows commandline? [Corion]: thezip: start "some title" path\to\that\ application, but that will open another console window

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (14)
As of 2017-03-27 18:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Should Pluto Get Its Planethood Back?

Results (321 votes). Check out past polls.