in reply to Re: Perl Hashes, keys under keys (I think)?
in thread Perl Hashes, keys under keys (I think)?

Absolutely, you can only have one record of a given key. So you can tweak the key like you are doing, or rethink your structure. Maybe you want the WAITING keys to be arrayrefs, pointing to lists of zero or more WAITING records.

Also, you have a lot of duplicate code. Consider consolidating like this, untested:

if ($fields[5] =~ /(OWNER|WAITING)/) { my $state = $1; # and perhaps this, for your new solution: $state .= $count if $state eq 'WAITING'; $x++; @fields = split " ", $temp[$x]; print "$state --- @fields\n"; #Add record to hash based on RECORD_ID $records{"$fields[1]"}{ $state } = { USER => "$fields[5]", FILENAME => "$fields[0]", PID => "$fields[6]", TIME => "$fields[9]", DATE => (join " ", "$fields[10] $fields[11]"), ELAPSED => [] }; #Increment to Next Line $x++; }

Replies are listed 'Best First'.
Re^3: Perl Hashes, keys under keys (I think)?
by mmartin (Monk) on Sep 20, 2011 at 19:27 UTC

    Hey hbm,

    Thanks for the reply. Good idea! You guys are so good at condensing code down to the bare minimum. I always seem to have a case of "code bloat".

    Only thing is, is that it will only do up to one WAITING user. That was one of the problems I ran into as well. Somewhere there would have to be something where it should, for instance, "Once you do a WAITING record, keep doing it until the line contains 'OWNER'".

    But really thanks for the suggestion. I will play around with your condensed version and see if I can get it to run properly.



    Thanks Again,
    Matt



    .

      Try this. Note that I changed it so that OWNER and WAITING are arrayrefs, hence the push. You'll probably want to add data validation too...

      Update:I made two late adjustments after 'strict'.

      use strict; use warnings; use Data::Dumper; my %records; my $state; # UPDATE - Added this! while(<DATA>){ my @fields = split; # and added 'my' if ($fields[5] =~ /(OWNER|WAITING)/) { $state = $1; } else { push@{$records{$fields[1]}{$state}}, { USER => $fields[5], FILENAME => $fields[0], PID => $fields[6], TIME => $fields[9], DATE => "$fields[10] $fields[11]", ELAPSED => [] }; } } print Dumper(%records);

      Partial Output:

      $VAR21 = '001!SCHEDULE'; $VAR22 = { 'WAITING' => [ { 'PID' => '33', 'TIME' => undef, 'DATE' => ' ', 'ELAPSED' => [], 'FILENAME' => '/ud/QC-DATA/CONTROL', 'USER' => 'marshall' }, { 'PID' => '4565', 'TIME' => undef, 'DATE' => ' ', 'ELAPSED' => [], 'FILENAME' => '/ud/QC-DATA/CONTROL', 'USER' => 'steven' } ], 'OWNER' => [ { 'PID' => '500', 'TIME' => undef, 'DATE' => ' ', 'ELAPSED' => [], 'FILENAME' => '/ud/QC-DATA/CONTROL', 'USER' => 'richard' } ] };
Re^3: Perl Hashes, keys under keys (I think)?
by mmartin (Monk) on Sep 20, 2011 at 20:09 UTC

    Hey hbm,

    I think I got it working correctly. All's I really had to do was add a couple of lines (i.e. add $count++, as well as a "else" clause, etc...).

    Here's what I got:

    if ($fields[5] =~ /(OWNER|WAITING)/) { $x++; $state = $1; if ($state eq 'OWNER') { $count = 0; } if ($state eq 'WAITING') { $state .= "-$count"; $count +++; } @fields = split " ", $temp[$x]; print "$state --- @fields\n"; #Add record to hash based on RECORD_ID $records{"$fields[1]"}{ $state } = { USER => "$fields[5]", FILENAME => "$fields[0]", PID => "$fields[6]", TIME => "$fields[9]", DATE => (join " ", "$fields[10] $fields[11]"), ELAPSED => [] }; #Increment to Next Line $x++; } else { $records{"$fields[1]"}{ "WAITING-$count" } = { USER => "$fields[5]", FILENAME => "$fields[0]", PID => "$fields[6]", TIME => "$fields[9]", DATE => (join " ", "$fields[10] $fields[11]"), ELAPSED => [] }; $count++; $x++; }



    Thanks Again,
    Matt


    .

      Matt--

      Consider reading your data one line at a time, rather than slurping into an array and manipulating $x.

      Also, here:

      DATE => (join " ", "$fields[10] $fields[11]"),

      The double-quotes on the right side bind the tenth and eleventh fields into a single string, which you then join(?) with a space. Joining a single item doesn't do anything; you can simply do:

      DATE => "$fields[10] $fields[11]"

        Hey hbm, thanks for the reply.

        I tried reading the data into the hash at first but it was just confusing the crap out of me (trying to use @_ or $_ and also $line) so I did the array thing just to get myself started. Once I get more comfortable with hashes, most likely by the end of this, I will probably go back and change it to what you suggested.

        haha Yeah your right, I guess it is pointless to use "join" when only joining 2 elements. Good point, thanks.


        I was working on this thing below that I am kinda stuck on. Maybe you could see what I am not doing right.
        What I want to do is: I have this new SUB, also a new array (@colLens used this method before with a 2D-Array and it worked good). @colLens has 0..7 elements, same as the amount of fields per each record.

        What it will do is, loop through the hash and if the current hash element's length is greater than the value in the same position in @colLens, then that element in @colLens will now big the bigger of the two. So the end effect will be that @colLens holds the largest length of each hash element 0..7 .


        Here's what I tried:
        my @header = ("RECORD_ID", "STATUS", "PID", "TIME", "DATE", "HELD/WAIT +ING", "FILENAME", "USER"); my @colLens; sub getColumnLengths() { for (my $y = 0; $y <= $#header; $y++) { $colLens[$y] = length($header[$y]); } my $x; # $rec1 = RECORD_ID for my $rec1 ( keys %records ) { $x = 0; if (int($colLens[$x]) < length($rec1)) { $colLens[$x] = length($rec1); } # $rec2 = STATUS for my $rec2 ( keys %{ $records{$rec1} } ) { $x = 1; if (int($colLens[$x]) < length($rec2)) { $colLens[$x] = length($rec2); } $x++; # $rec3 = PID, TIME, DATE, ELAPSED, FILENAME, USER for my $rec3 ( keys %{$records{$rec1}{$rec2}} ) { if (int($colLens[$x]) < int(length($rec3))) { $colLens[$x] = int(length($rec3)); } $x++; }#END INNER-MOST }#END MID }#END OUTER }

        From my output I get it seems that it is working for just the first 2 elements, not sure why the rest isn't? I'm assuming it is something with my inner-most loop...


        If you can think of anything please let me know.

        Thanks in Advance,
        Matt