http://www.perlmonks.org?node_id=1192550


in reply to Re: Can Text::CSV_XS return key-value pairs?
in thread Can Text::CSV_XS return key-value pairs?

Tux, can I add that to what I already have? Also, the value will not always be split. So can the value be first put through the filters then go to the on_in? And if this needs to use an additional hash, then how do I get everything to use the outside hash, the one you called %foo?

sub make_list { my (%opt) = @_; my $file = $opt{file} && ref($opt{file}) eq 'ARRAY' ? data_file(@{$o +pt{file}}) : $opt{file}; my $headers = $opt{'headings'} ? $opt{'headings'} : ['heading']; my $filters; for my $header (@$headers) { if ($header =~ s/\+$//) { $filters->{$header} = sub { $_ = [split(/;\s*/, $_)]; $_ }; } } my $list = csv ( in => $file, headers => $headers, key => $opt{key} ? $headers->[0] : undef, filter => $filters, sep_char => '|', quote_char => undef, empty_is_undef => 1, allow_whitespace => 1, auto_diag => 1, ); return $list; }

I did not want to include the code I am trying to replace with an all in one, but it looks like I have to.

sub make_hash { my %opt = @_; my $file = $opt{file} && ref($opt{file}) eq 'ARRAY' ? data_file(@{$o +pt{file}}) : $opt{file}; open(my $fh, '<', $file) || die "Can not open $file $!"; # If there are no headings, then 'heading' is the only heading. my @headings = $opt{headings} ? @{$opt{headings}} : ('heading'); my %hash; while (my $line = <$fh>) { chomp $line; my @values = split(/\|/,$line); # the key is always the first value, though if only one heading, t +hen only 1 value needed. my $key = scalar @headings > 1 ? $values[0] : shift @values; my $n = 0; for my $r_heading (@headings) { # skip any heading without a value if (defined($values[$n]) && length($values[$n]) > 0) { # split values if heading ends with a + my $split = $r_heading =~ /\+$/ ? 1 : 0; # but don't keep the + in the heading (my $heading = $r_heading) =~ s/\+$//; my $value = $split == 1 ? [map { $_ =~ s/^ //; $_ } split(/;/, +$values[$n])] : $values[$n]; # if more than 1 heading, make an HoH; only 1 heading, make H if (scalar @headings > 1) { $hash{$key}{$heading} = $value; } else { $hash{$key} = $value; } } $n++; } } return \%hash; } # This is simpler to convert, all I need to do is omit the key. # I didn't account for the possible splitting of the value because I h +adn't needed it. sub make_array { my %opt = @_; my $file = $opt{file} && ref($opt{file}) eq 'ARRAY' ? data_file(@{$o +pt{file}}) : $opt{file}; open(my $fh, '<', $file) || die "Can not open $file $!"; my @array; while (my $line = <$fh>) { chomp $line; my %hash; my @values = split(/\|/,$line); @hash{@{$opt{headings}}} = @values; push @array, \%hash; } return \@array; }
No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena

Replies are listed 'Best First'.
Re^3: Can Text::CSV_XS return key-value pairs?
by Tux (Abbot) on Jun 13, 2017 at 15:06 UTC

    Like this then? (only requirement is to have "item" a required field

    use 5.18.2; use warnings; use Data::Peek; use Text::CSV_XS qw(csv); my $file = "pm-1192516.csv"; sub make_list { my %opt = @_; my %plus; my @head = map { s/\+$// && $plus{$_}++; $_ } @{$opt{headings}}; my %foo; my $list = csv ( in => $opt{file}, headers => \@head, sep_char => "|", quote_char => undef, empty_is_undef => 1, allow_whitespace => 1, auto_diag => 1, on_in => sub { foreach my $f (@head) { $f eq "item" and next; push @{$foo{$_{item}}{$f}}, $plus{$f} ? split m/;\s*/ +=> $_{$f} : $_{$f}; } }, ); return \%foo; } DDumper make_list (file => $file, headings => [ "item", "seen in+" ]);

    ->

    { 'Ark of the Covenant' => { 'seen in' => [ 'Raides of the Lost Ark', 'The Librarian: Quest for the Spear' ] }, 'Book or Key of Soloman' => { 'seen in' => [ 'The Librarian: Return to King Soloman\'s Mines', 'Season of the Witch' ] }, 'Crystal Skull' => { 'seen in' => [ 'Stargate SG-1, Crystal Skull', 'The Librarian: Return to King Soloman\'s Mines', 'Indiana Jones and the Kingdom of the Crystal Skull' ] }, 'Doc Brown\'s Delorean' => { 'seen in' => [ 'Back to the Future', 'Back to the Future Part II', 'Back to the Future Part III', 'The Librarians, And the Final Curtain' ] }, Excalibur => { 'seen in' => [ 'Excalibur', 'The Last Legion', 'The Librarian: Quest for the Spear' ] }, 'H.G. Wells\' Time Machine' => { 'seen in' => [ 'The Librarians', 'Warehouse 13' ] }, 'Holy Grail' => { 'seen in' => [ 'Indiana Jones and the Last Crusade', 'The Librarian: Quest for the Spear' ] }, Necronomicon => { 'seen in' => [ 'H.P. Lovecraft' ] }, 'Pandora\'s Box' => { 'seen in' => [ 'Warehouse 13', 'The Librarian: Quest for the Spear' ] }, 'Spear of Destiny' => { 'seen in' => [ 'The Librarian: Quest for the Spear', 'Hellboy', 'Constantine' ] }, TARDIS => { 'seen in' => [ 'Doctor Who', 'The Sarah Jane Adventures', 'The Librarians, And the Final Curtain' ] } }

    Enjoy, Have FUN! H.Merijn

      More like the following without the need of a required first field. I took out the push since it was making every field an array ref. Also, this does both a hash and hash of hashes. Now to get arrays of hashes, and I'd have my all in one. 8)

      It took me this long to figure all of this out, but I had a sick cat to deal with too. My sick cat takes precedence over my perl code.

      sub make_list { my %opt = @_; my $file = $opt{file} && ref($opt{file}) eq 'ARRAY' ? data_file(@{$o +pt{file}}) : $opt{file}; my $raw_headers = $opt{'headings'} ? $opt{'headings'} : ['heading']; unshift @$raw_headers, 'key' if scalar @$raw_headers == 1; my %exapansions; my $headers = [map { s/\+$// && $exapansions{$_}++; $_ } @$raw_heade +rs]; my %list; my $csv = csv ( in => $file, headers => $headers, sep_char => '|', quote_char => undef, empty_is_undef => 1, allow_whitespace => 1, auto_diag => 1, on_in => sub { for my $header (@$headers) { if (scalar @$headers == 2) { $header eq $headers->[0] and next; $list{$_{$headers->[0]}} = $exapansions{$header} ? [split(m/ +;\s*/, $_{$header})] : $_{$header}; } else { $list{$_{$headers->[0]}}{$header} = $exapansions{$header} ? +[split(m/;\s*/, $_{$header})] : $_{$header}; } } } ); return \%list; }

      Gives me ...

      And this (file)...

      And even this (file)...

      No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
      Lady Aleena