1

I'm new to Perl. I'm trying to create a hash from a CSV file.

My CSV data currently looks like this:

id,name,title,rating
123,Andrew,Book 1,3
1221,Abraham,Book 2,4
43,Annie,Book 3,1

I'd like the hash to look like this

$reviews =  {
     review => [
                 {
                    id     => [ 123 ],
                    name   => [ Andrew ],
                    title  => [ "Book 1" ],
                    rating => [ 3 ],
                 },
                 {
                    id     => [ 1221 ],
                    name   => [ Abraham ],
                    title  => [ "Book 2" ],
                    rating => [ 4 ]]
                 },
                 {
                    id     => [ 43 ],
                    name    => [ Annie ],
                    title   => [ "Book 3" ],
                    edition => [ 1 ],
                 },
              ]
           };

But I'm getting this instead

$VAR1   =  {
     '123' => { 
                  'name' => 'Andrew',
                  'title' => 'Book 1',
                  'id' => '123',
                  'rating' => '3',
               },
     '1221' => { 
                  'name' => 'Abraham',
                  'title' => 'Book 2',
                  'id' => '1221',
                  'rating' => '4',
               },
     '43' => { 
                  'name' => 'Annie',
                  'title' => 'Book 3',
                  'id' => '43',
                  'rating' => '1',
               }

        };

Here's the code I'm using so far. My CSV is in the output.csv file and I'm printing the hash in the hashr.txt file

my %hash;
open (RESULTS, "output.csv")|| die "Can't open output.csv: $!\n";
open (HASHR, "+>hashr.txt")|| die "Can't open hashr.txt: $!\n";

while (<RESULTS>) {
    last if /id/
}
my $labels = $_; #save last line to label keys
chop $labels;

while (<RESULTS>) {
    chomp;
    my @array = split /,/;
    my $index = 0;
    my %h = map { $_ => $array[$index++]} split( ",", $labels );

    #my $key = "review";
    #$hash{$key}=\%h;

    $hash{ $array[0] } = \%h;
}

print Dumper(\%hash);
print HASHR Dumper(\%hash);
close RESULTS;
2
  • 1
    Are you looking for the values to really be [] which is an anonymous array in perl? Commented Jun 20, 2014 at 5:42
  • I think what you have already is much better than the design you're aiming for. Do you realise that $reviews is a one-element hash, and that your hash values are one-element arrays? So to access, say, the name field of the second review you have to write $reviews->{review}[1]{name}[0]. Unless you have more data that you need to store in the same structure, wouldn't it be better if $reviews was an array reference and your hash values were plain strings? That way, accessing the same item would look like $reviews->[1]{name} which is much simpler and less prone to bugs. Commented Jun 20, 2014 at 12:10

2 Answers 2

1

Your desired data structure is strange, but the following should get you closer to what you say you want.

You probably could use a refresher of perldsc to learn more about Complex Data Structures.

use strict;
use warnings;

my $header = <DATA>;
chomp $header;
my @headers = split /,/, $header;

my @records;
while (<DATA>) {
    chomp;
    my @cols = split /,/;
    my %hash;
    @hash{@headers} = map [$_], @cols;
    push @records, \%hash;
}

use Data::Dump;
dd \@records;

__DATA__
id,name,title,rating
123,Andrew,Book 1,3
1221,Abraham,Book 2,4
43,Annie,Book 3,1

Outputs:

[
  { id => [123], name => ["Andrew"], rating => [3], title => ["Book 1"] },
  { id => [1221], name => ["Abraham"], rating => [4], title => ["Book 2"] },
  { id => [43], name => ["Annie"], rating => [1], title => ["Book 3"] },
]
Sign up to request clarification or add additional context in comments.

3 Comments

@Borodin I realize I probably shouldn't do this, but I'll sometimes adjust my coding style preferences just for SO in order to cater to the syntax highlighter. I probably do this most often with regular expressions, leaning more strongly to s{}{} versus s/// when the syntax highlighter messes things up like it does above with // in a split not followed by a semicolon. Can't win everywhere, I suppose :)
I sympathise, but Perl is notorious for its best-guess parsing and a proper highlighter would have to compile the code to get it right. That makes it impossible to properly highlight anything that doesn't compile. I would be inclined to write best-practice code and just remove the highlighting if necessary with <!-- language: lang-none -->. After all, the highlighter is bound to make a mess of anything in __DATA__. In any case the default highlighting uses such muted colours that I generally overlook it
Yeah, poor syntax highlighting is pretty minor, but it still bugs me occasionally. The thing is, there are better parsers, like the one employed by my editor Sublime Text. Almost makes me want to contribute to Google Prettifier, but not likely to invest my time there right now :)
0
review => [
             {
                id     => [ 123 ],
                name   => [ Andrew ],
                title  => [ "Book 1" ],
                rating => [ 3 ],
             },

'123' => { 
              'name' => 'Andrew',
              'title' => 'Book 1',
              'id' => '123',
              'rating' => '3',
           },


my %h = map { $_ => $array[$index++]} split( ",", $labels );


to

my %h = map { $_ => @{$array[$index++]}} split( ",", $labels );

It's been years since I've touched perl, so that syntax is probably way off but the gist is you'll want to place the values generated into an array, then wrap it all in an array

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.