DunLidjun has asked for the wisdom of the Perl Monks concerning the following question:

I have a dataset that I'm trying to put into a fairly complete data structure, with little success. The first thing I'm doing is using the number (column 1) to tell the level of the part number (column 2). I'm creating a path by building up the levels until the level either stays the same or decreases.

The data file has the following headings: Level;Description;Required;Lot;Issued;Unitcost;TotalCost

If I run the code below (commenting out the DiveVal lines) I can come up with the path I need for the hash, but I'm stuck there.

What I'm trying to get is a hash like:

$structure{0009400000}{0009400ENG}{0000000002}{Required}=1.00 $structure{0009400000}{0009400ENG}{0000000002}{Lot}=[362475] $structure{0009400000}{0009400ENG}{0000000002}{Issued}=[1.0] $structure{0009400000}{0009400ENG}{0000000002}{UnitCost}=[1.0] $structure{0009400000}{0009400ENG}{0000000002}{TotalCost}=[1.0] $structure{0009400000}{0009400MFG}{&NI4820}{Required}=1.0 $structure{0009400000}{0009400MFG}{1003505000}{Required}=1.0 $structure{0009400000}{0009400MFG}{3102517000}{3102517001}{Required}=1 +00.00 $structure{0009400000}{0009400MFG}{3102517000}{3102517001}{Lot}=[35837 +9 366268] $structure{0009400000}{0009400MFG}{3102517000}{3102517001}{Issued}=[50 +.0 50.0] $structure{0009400000}{0009400MFG}{3102517000}{3102517001}{UnitCost}=[ +0.4452 0.5558] $structure{0009400000}{0009400MFG}{3102517000}{3102517001}{TotalCost}= +[22.26 27.84]

Any help would be appreciated.

The code:

#!/usr/bin/perl # use strict; use warnings; use File::Find; use Data::Dumper; use Data::Diver qw (DiveVal); my @extracted=<DATA>; $extracted[$#extracted+1]="end"; my @currentpath=(); my $structure={}; foreach my $i (0..$#extracted) { my @list=split /;/, $extracted[$i]; my @list2=split /;/, $extracted[$i+1]; if ($list2[0] eq 'end') { last; } if ($list2[0] gt $list[0]) { push @currentpath,$list[1]; print "=====\n$i\n====\n"; } elsif ($list2[0] eq $list[0]) { my $temp=join( ':',@currentpath); $temp=join(':',$temp,$list[1]); my $required=join(":",$temp,"required"); my @req=split /:/,$required; print "$temp\n"; DiveVal($structure, @req)= $list[3]; } elsif ($list2[0] lt $list[0]) { my $temp=join( ':',@currentpath); $temp=join( ':',$temp,$list[1]); my $required=join(":",$temp,"required"); my @req=split /:/,$required; print "$temp\n"; DiveVal($structure,@req)= $list[3]; pop @currentpath; } } print Dumper($structure); __DATA__ 0;0009400000;MASTERPAK 22,S/N 9400;1.00;379040;1.00;; 1;0009400ENG;LABOR,ENG,MASTERPAK 22;1.00;379039;1.00;; 2;0000000002;REFERENCE INFORMATION;1.00;362475;1.00;0.0000;0.00 1;0009400MFG;MASTERPAK 22,S/N 9400;1.00;379038;1.00;; 2;&NI4820;;0.00;0;1.00;32.5000;32.50 2;1003505000;PUMP,CNTFGL,02.000"FLG;1.00;367879;1.00;5692.8200;5692.82 2;3102517000;NAMETAG,VLV/INST,BIOPHARM;70.00;371584;70.00;; 3;3102517001;TAG,BLANK,1.5"DIA X .038";100.00;358379;50.00;0.4452;22.2 +6 3;3102517001;TAG,BLANK,1.5"DIA X .038";;366268;50.00;0.5568;27.84 3;3102517002;LABEL,CLEAR,1"W X .5"H;100.00;298010;100.00;0.0124;1.24 2;3102519000;NAMETAG,EQUIP COMPONENTS;20.00;371583;20.00;; 3;3102519001;LABEL,WHITE,3"W X 3.5"H;20.00;358124;20.00;1.0664;21.33

Replies are listed 'Best First'.
Re: Parsing a dataset into an arbitrary sized hash of hashes
by choroba (Cardinal) on Apr 02, 2014 at 22:32 UTC
    The problem seems to be the absence of types in Perl: if DiveVal encounters a number, it creates an array for you, not a hash:
    $ perl -MData::Dumper -MData::Diver=DiveVal -E 'DiveVal($x = {}, qw(1 +2 3)); say Dumper $x' $VAR1 = { '1' => [ undef, undef, [ undef, undef, undef, undef ] ] };

    BTW, I had to rewrite your code to be able to understand what it does:

    Update: See Re^3: Parsing a dataset into an arbitrary sized hash of hashes on how to fix the problem (line 28 of my code).

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      The problem seems to be the absence of types in Perl: if DiveVal encounters a number, it creates an array for you, not a hash:

      :) The documentation disagrees -- you can specify \2 if you want to force hash keys

      Note that all 'keys' that work for arrays also work for hashes. If you have a reference that is overloaded such that it can both act as an array reference and as a hash reference or, in the case of DiveVal() and DiveRef(), if you have an undefined $ref which can be autovivified into either type of reference, then numeric-looking key values cause an array dereference. In the above cases, if you want to do a hash dereference, then you need to pass in a reference to the key.
        Great! The following change then solves the problem:
        my @req = map \$_, @currentpath, $list[1], 'required';
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ