Unable to get desired hash for perl CSV format

I exported the most common emp table to CSV. I would like to take each column name as a hash key and store the values ​​in an array.

Below is the code

use Data::Dumper; open($fh,"<EMP.csv"); %hash = (); local $/= "\n"; while(<$fh>){ @columnNames = split(/,/,$_) if $. ==1; @columnValues = split(/,/,$_); push @{hash->{@columnNames}} ,@columnValues; } print Dumper(\%hash); 

when i try to print the hash i get this

 $VAR1 = { '8' => [ '"EMPNO"', '"ENAME"', '"JOB"', '"MGR"', '"HIREDATE"', '"SAL"', '"COMM"', '"DEPTNO" ', '"7839"', '"KING"', '"PRESIDENT"', '""', '"11/17/1981"', '"5000"', '""', '"10" ', 

But I expect this instead

 $VAR1 = { '"EMPNO"'=>[12,3,4,5,6,7,8,9], '"EMPNAME"'=>["pavan","kumar"...], }; 
+4
source share
3 answers

You are trying to use a slice in a push statement, and this will not work. The array will be in a scalar context, so you see key 8 . You will need to flip the keys to push values ​​to arrays. But why do this?

You can use Text::CSV for this, which is pretty simple and probably more appropriate if you have a real csv format.

 use strict; use warnings; use Data::Dumper; use Text::CSV; my $csv = Text::CSV->new({ binary => 1, eol => $/, }); my %data; open my $fh, "<", "yourfile.csv" or die $!; $csv->column_names ($csv->getline($fh)); # get header names while (my $row = $csv->getline_hr($fh)) { # get hashref with values for my $key (keys %$row) { push @{$data{$key}}, $row->{$key}; # store values } } print Dumper \%data; 
+5
source

Rewrite this line

 push @{hash->{@columnNames}} ,@columnValues; 

in

 foreach my $columnName (@columnNames) { my $columnValue = shift @columnValues; push @{ $hash{$columnName} }, $columnValue; } 
+1
source

Here is your problem:

 push @{hash->{@columnNames}} ,@columnValues; 

You are trying to use @columnNames as the key in your hash. Perl automatically takes this in a scalar context, thereby giving you a key of 8 , since there are eight values ​​in the array.

What you want to do is treat the first row in CSV (which contains the column names) as special, as these will be the keys to your array.

 my @column_names = read_csv_row; #Column names my %employee_hash; for my $column ( @column_names ) { $employee_hash{$column} = []; } 

This will give you a hash with a key by column name to array references. Now you need to read in each row of the CSV table and click each field in its correct column hash;

 while ( my @employee_fields = read_csv_row ) { #Your employee record is in @employee for my $field_num ( 0..$#employee_fields) { push @{ $employee_hash{$column_names[$field_num] }, $employee_fields[$field_num]; } } 

What this does is take each field from the CSV string and paste it into the correct array reference in %employee_hash . I understand that @column_names is in the same order as each row. So $column_names[$field_number] is the correct hash key, and this should match $employee_fields[$field_num] .

However, the structure you said in your post is probably not what you really want. You want something like this:

 %VAR = { 7839 => { ENAME => "KING", JOB => "PRESIDENT", MGR => "", HIREDATE => "11/17/1981", SAL => "5000", COMM => "", DEPTNO => "10", } } 

This wil key for each employee by their employee number, and all employee fields associated with it will be part of this value. Then you can talk about the job title Employee number 7839 as $employee{7839}->{JOB} , and the employee name is $employee{7839}->{NAME} . Thus, all information about each employee is in one record:

 use warnings; use strict; use Data::Dumper; use feature qw(say); my @column_names = read_csv_row(); #Column name my %employee_hash; while ( my @minion_fields = read_csv_row() ) { #Your employee record is in @employee my %minion_hash; my $minion_number = $minion_fields[0]; for my $field_num ( 1..$#minion_fields) { $minion_hash{ $column_names[$field_num] } = $minion_fields[$field_num]; } $employee_hash{$minion_number} = \%minion_hash; } sub read_csv_row { my $row = <DATA> or return; chomp $row; return split /,\s+/, $row; } say Dumper \%employee_hash; __DATA__ empno, name, job, mgr, hiredate, sal, comm, deptno 7839, king, president, , 11/17/1981, 5000, , 10 1234, prince, vice-president, , 10/1/1980, 3000, , 10 

By the way, I have not tested this code yet. (I will do it now and make the necessary corrections). You might want to play around with Text::CSV , which would be the best way to read in CSV files, and might even help you create these structures (I haven't used it for a long time, so I don't remember everything that it does). However, I believe that you will find that the structure of your employee is a hash of the hashes with the initial hash entered by the employee number, and subahels entered using fields are much better than the hash of arrays.

+1
source

Source: https://habr.com/ru/post/1489449/


All Articles