tgerv
tgerv

Reputation: 11

Unable to read multiple columns from a .csv using Text::CSV_XS in Perl

I'm trying to write a script that will separate out columns of data from a (not terribly large) .csv into individual lists for use later using the Text::CSV_XS library. I have no problem getting individual columns, but I seem to be unable to iterate through a list of columns using a foreach loop.

#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV_XS;
use 5.18.2;

my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1 });
open my $fh, "<", "/users/whoever/test_csv.csv" or die "$!";

sub column_grabber {
        foreach my $column (@_) {
                my @data = map { $_->[$column] } @{$csv->getline_all ($fh)};
                return @data;
        }
}

my @column_numbers = (1,2,3,4);

my @collected_data = column_grabber(@column_numbers);

close $fh or die "$!";

Calling this subroutine for a list of columns gives me only the first column of the list as anticipated, but none of the following columns from the list. A bit of troubleshooting shows that @_ is seeing the entire list I pass.

By omitting the return statement, the foreach loop carries through all of the columns passed in @ids, but I get no output from @data.

Is there some behavior in the loop I'm not accounting for? Perhaps it has something to do with the map() statement?

Edit / Solution

So after playing around with this for a while and rethinking things a bit, I've solved my problem.

So now my functioning script looks like this:

#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV_XS;
use 5.18.2;

sub column_grabber {
    my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1 });
    open my $fh, "<", "/users/whoever/test_csv.csv" or die "$!";

    my $column = shift @_;
    my @data = map { $_->[$column] } @{$csv->getline_all ($fh)};
    return @data;

    close $fh or die "$!";
}

my @column_numbers = (1,2,3,4);
foreach my $column(@column_numbers){
    my @collected_data = &column_grabber($column);
...
}

Thanks for the input and help from commenters.

Upvotes: 1

Views: 161

Answers (1)

ikegami
ikegami

Reputation: 386696

Keep in mind that each element of @data (hereby renamed @rows or $rows) should be a reference to an array of the selected fields.

my @rows;
while ( my $row = $csv->getline($fh) ) {
   push @rows, [ @{ $row }[@column_numbers] ];
}

or

my $rows = $csv->getline_all($fh);
@_ = @{ $_ }[@column_numbers] for @$rows;

or

my @rows = map { [ @{ $_ }[@column_numbers] ] } @{ $csv->getline_all($fh) };

Upvotes: 2

Related Questions