Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Perl-ish way to create hash from array

by gri6507 (Deacon)
on Jul 09, 2013 at 17:20 UTC ( [id://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?

Replies are listed 'Best First'.
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 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 LanX (Saint) 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 tangent (Parson) 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 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)};
Re: Perl-ish way to create hash from array
by rjt (Curate) 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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (6)
As of 2024-04-20 00:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found