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

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

I'll say straight up I struggle with hashes and this is proving especially so with this challenge.

My _DATA_ is shown below - it's the output from a system command and the columns are space seperated. Pseudo hash structure shown below as to how I believe I need the output stored in order for me to make sense of the data in my code. Which is to be able to read the status for each subsystem/daemon and also sort all daemons by their status.

But I cannot get me head around how to construct the required hash from my _DATA_ can anyone assist please?

$VAR1 = { 'inted' => { 'Group' => 'tcpip', 'PID' => '2424886', 'Status' => 'active' } 'xntpd' => { 'Group' => 'tcpip', 'PID' => '3473550', 'Status' => 'active' } 'rwhod' => { 'Group' => 'tcpip', 'PID' => '', 'Status' => 'inoperative' } }; _DATA_ Subsystem Group PID Status inetd tcpip 2424886 active xntpd tcpip 3473550 active rwhod tcpip inoperative snmpd tcpip inoperative aixmibd tcpip inoperative hostmibd tcpip inoperative snmpmibd tcpip inoperative

Replies are listed 'Best First'.
Re: Hash creation problem
by BrowserUk (Patriarch) on Feb 19, 2014 at 11:21 UTC

    #! perl -slw use strict; use Data::Dump qw[ pp ]; my( undef, @keys ) = unpack 'A18 A17 A13 A*', <DATA>; my %hash; while( <DATA> ) { chomp; my( $id, @fields ) = unpack 'A18 A17 A13 A*'; @{ $hash{ $id } }{ @keys } = @fields; } pp \%hash; __DATA__ Subsystem Group PID Status inetd tcpip 2424886 active xntpd tcpip 3473550 active rwhod tcpip inoperative snmpd tcpip inoperative aixmibd tcpip inoperative hostmibd tcpip inoperative snmpmibd tcpip inoperative

    Output:

    C:\test>junk17 { " aixmibd" => { Group => "tcpip", PID => "", Status => "inoperative +" }, " hostmibd" => { Group => "tcpip", PID => "", Status => "inoperative +" }, " inetd" => { Group => "tcpip", PID => 2424886, Status => "active +" }, " rwhod" => { Group => "tcpip", PID => "", Status => "inoperative +" }, " snmpd" => { Group => "tcpip", PID => "", Status => "inoperative +" }, " snmpmibd" => { Group => "tcpip", PID => "", Status => "inoperative +" }, " xntpd" => { Group => "tcpip", PID => 3473550, Status => "active +" }, }

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Hash creation problem
by hippo (Bishop) on Feb 19, 2014 at 11:22 UTC

    Your structure there is a hash of hashes (HoH), see perldsc for details of data structures like this. To construct it:

    1. Read first line of data, split on whitespace and store in an array. These are your field names.
    2. Read next line of data and split on whitespace. First column is outer key, columns 2 .. n are the values to be associated with the equivalent key names in the array from step 1 above.
    3. Repeat step 2 until eof.

    Try that and follow-up here with your code if you get stuck. Note that arrays start from zero in Perl, so the second column will actually be $array[1] in your code, etc. HTH.

    Update: Splitting on whitespace could give you problems with the missing fields. Unless you have well defined field delimiters, follow BrowserUK's fixed-column approach above when reading instead.

Re: Hash creation problem
by kevbot (Vicar) on Feb 19, 2014 at 16:09 UTC

    Others have given you good suggestions already. I just wanted to show that you could use the Text::xSV::Slurp module to create the HoH structure for you. The whitespace separated data would be a problem for this module, so you would need your input data to be in a delimited format for this approach to work (like csv shown below). BrowserUK has shown how to deal with the whitespace separated data that you have. I would encourage you to become very familiar with how to use hashes in perl before resorting to a module to do the work for you, since they are such an important feature of perl.

    This code:

    #!/usr/bin/env perl use strict; use warnings; use Text::xSV::Slurp; my $hoh = xsv_slurp( \*DATA, shape => 'hoh', key => 'Subsystem', ); use Data::Dumper; print Dumper $hoh; exit; __DATA__ Subsystem,Group,PID,Status inetd,tcpip,2424886,active xntpd,tcpip,3473550,active rwhod,tcpip,,inoperative snmpd,tcpip,,inoperative aixmibd,tcpip,,inoperative hostmibd,tcpip,,inoperative snmpmibd,tcpip,,inoperative

    Gives the output:

    $VAR1 = { 'inetd' => { 'Group' => 'tcpip', 'Status' => 'active', 'PID' => '2424886' }, 'hostmibd' => { 'Group' => 'tcpip', 'Status' => 'inoperative', 'PID' => '' }, 'snmpmibd' => { 'Status' => 'inoperative', 'PID' => '', 'Group' => 'tcpip' }, 'aixmibd' => { 'PID' => '', 'Status' => 'inoperative', 'Group' => 'tcpip' }, 'snmpd' => { 'Group' => 'tcpip', 'Status' => 'inoperative', 'PID' => '' }, 'xntpd' => { 'Group' => 'tcpip', 'Status' => 'active', 'PID' => '3473550' }, 'rwhod' => { 'Group' => 'tcpip', 'Status' => 'inoperative', 'PID' => '' } };

      I would encourage you to now write the program that will take the OP's data in the form he has it and then convert it to the form that your "solution" requires it to be in to work.

      And then see how much time you saved using a module that solved a different problem.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        I just wanted to show an alternative. I think the solution you posted is the most useful for the specific question at hand. Perhaps the OP has some control over the format of the input data. What if they had the means to quickly obtain the input data in delimited format (e.g. a command line switch)? Then maybe using Text::xSV::Slurp makes sense. Also, the module has additional features that may prove useful for the OP in the future.
Re: Hash creation problem
by sundialsvc4 (Abbot) on Feb 19, 2014 at 14:00 UTC

    There is a Perl feature that is specifically designed to deal with this sort of thing:   it goes by the somewhat fancy-pants moniker, auto-vivification, which is “to spring to life automatically.”   Consider these two statements:

    my $foo = undef; $foo->{'bar'}{'bletch'}{'sneeze'} ++;
    A lot of automatic voodoo “just happens” here.   $foo immediately becomes a hashref; it acquires the key 'bar' which also becomes a hashref, which itself now has the key 'bletch'; ditto 'sneeze'; and this element automatically becomes an integer, automatically zero, which is incremented to 1.

    Therefore, in Perl it requires very little code at all in order to construct arbitrarily-complex reference based data structures in memory.   Or, thanks to a powerful reference-count based memory manager, to maintain those structures without leaks.   There are many aspects of Perl that are “what all the fuss is about,” and this is certainly one of them.

      Unfortunately it's this voodoo that I just have failed to get my head around.

      BrowserUK I really appreciate that and having read up on unpack can understand it too. hippo - thank you that helps.