Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Perl-ish way to create hash from array

by gri6507 (Deacon)
on Jul 09, 2013 at 17:20 UTC ( #1043357=perlquestion: print w/ replies, xml ) Need Help??
gri6507 has asked for the wisdom of the Perl Monks concerning the following question:

When processing CSV files, I usually use Text::CSV to read in the first line to get the column headings and then I use that information to index into the rest of the data by column names (I know what the columns are called, but I don't know what column number they are). Currently, I use the following code, which does not feel very Perl-ish.
use warnings; use strict; use Text::CSV; # setup a CSV parser my $csvParser = Text::CSV->new({ binary => 1, sep_char => ',', empty_i +s_undef => 1 }) or die "Cannot create new CSV parser: $!\n"; open my $csvHandle, "<", $csvFile or die "could not open $csvFile: $!" +; # figure out the column headings from the first line my @csvColumns = (); foreach my $colName (@{$csvParser->getline($csvHandle)}) { push @csvColumns, $colName; } # reverse that to allow lookup by name my %csvColumns; for my $colNum (0 .. scalar(@csvColumns)-1) { $csvColumns{$csvColumns[$colNum]} = $colNum; } # access some column in the CSV file by name while (my $line = $csvParser->getline($csvHandle)) { print "Column Foo has value $$line[$csvColumns{Foo}]; }
Is there a more perl-like way to reverse an array into a hash that could then be used to get the index into the data?

Comment on Perl-ish way to create hash from array
Download Code
Re: Perl-ish way to create hash from array
by moritz (Cardinal) on Jul 09, 2013 at 17:29 UTC
Re: Perl-ish way to create hash from array
by LanX (Canon) on Jul 09, 2013 at 17:29 UTC
    maybe hash-slices are what you want:

    DB<101> @columns=qw(firstname middlename lastname) => ("firstname", "middlename", "lastname") DB<102> @colidx{@columns}=0..$#columns => (0, 1, 2) DB<103> $colidx{middlename} => 1 DB<104> \%colidx => { firstname => 0, lastname => 2, middlename => 1 }

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Perl-ish way to create hash from array
by kennethk (Abbot) on Jul 09, 2013 at 17:30 UTC
    I'd consider it Perlish, because it gets the job done and is quite legible. However, you could do it on one line with a map if that feels Perlier to you:
    my %csvColumns = map {$csvColumns[$_] => $_} 0 .. $#csvColumns;
    Or you could use an explicit iterator with a do to limit scope:
    my %csvColumns = do {my $i = 0; map {$_ => $i++} @csvColumns};
    I know lots of folks would describe those as Perlier, though would not be using the phrase in a positive way.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Perl-ish way to create hash from array
by tangent (Curate) on Jul 09, 2013 at 19:14 UTC
    Maybe I'm not understanding your question but you can use Text::CSV to give you a hash reference directly:
    # figure out the column headings from the first line my $headrow = $csvParser->getline($csvHandle); # tell the parser to use those headings as hash keys $csvParser->column_names($headrow); while ( my $hash = $csvParser->getline_hr($csvHandle) ) { print "Column Foo has value $hash->{Foo}"; }
Re: Perl-ish way to create hash from array
by rjt (Deacon) on Jul 09, 2013 at 20:42 UTC

    As of Perl 5.12, each works on arrays:

    use 5.012; use warnings; my @array = qw/zero one two three four five six/; my %hash; while (my ($idx, $val) = each @array) { $hash{$idx} = $val };

    I wouldn't call this a "win" in this particular example over using a slice or Text::CSV's own API, but it's not bad, and in more complex situations it works handily.

Re: Perl-ish way to create hash from array
by PrakashK (Pilgrim) on Jul 09, 2013 at 20:58 UTC
    moritz and tangent have already answered your question. But, I would like to point out something in your code:

    Here, you are iterating through the list of column names and building a new list in @csvColumns array.

    # figure out the column headings from the first line my @csvColumns = (); foreach my $colName (@{$csvParser->getline($csvHandle)}) { push @csvColumns, $colName; }

    In your foreach loop, you are building a (temporary) list by dereferencing the return value of the getline method (@{$csvParser->getline($csvHandle)}), then iterating through this list and copying each element into the other list (push @csvColumns, $colName;), and finally discarding (implicitly) the temporary list.

    You could instead do:

    # figure out the column headings from the first line my @csvColumns = @{$csvParser->getline($csvHandle)};

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1043357]
Approved by Perlbotics
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (12)
As of 2015-07-02 22:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (46 votes), past polls