Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

lost in my data structure.

by lepetitalbert (Abbot)
on Nov 15, 2005 at 03:33 UTC ( #508466=perlquestion: print w/replies, xml ) Need Help??

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

Hi dear Monks

I'm trying to learn 'complex'(for me) data structs. I never use more than a key/value hash.

Now I need something like :

%host { 'ip1' => { 'tcp' => { '21' => { 'state' => open; 'service' => ftp; }, '80' => { 'state' => open; 'service' => web; } } } }, 'ip2' => { 'tcp' => { '23' => { 'state' => open; 'service' => telnet; }, '80' => { 'state' => open; 'service' => web; } } } } };

I have read hash tutorials for two days, but have still no idea how to loop through this to print :

ip1 21 open ftp 80 open web ip2 23 open telnet 80 open web

or how to add 53 open dns to ip1

Have a nice day

Replies are listed 'Best First'.
Re: lost in my data structure.
by jfroebe (Parson) on Nov 15, 2005 at 03:52 UTC
    #!/usr/bin/perl use strict; use warnings; my %host = ( 'ip1' => { 'tcp' => { '21' => { 'state' => 'open', 'service' => 'ftp', }, '80' => { 'state' => 'open', 'service' => 'web', } } }, 'ip2' => { 'tcp' => { '23' => { 'state' => 'open', 'service' => 'telnet', }, '80' => { 'state' => 'open', 'service' => 'web', } } } ); foreach my $key (keys %host) { print "$key\n"; foreach (keys %{ $host{$key}{tcp} } ) { printf " %s %s %s\n", $_, $host{$key}{tcp}{$_}{state}, $host{$key}{tcp}{$_}{service}; } }

    Results in:

    ip1 21 open ftp 80 open web ip2 80 open web 23 open telnet

    Jason L. Froebe

    Team Sybase member

    No one has seen what you have seen, and until that happens, we're all going to think that you're nuts. - Jack O'Neil, Stargate SG-1

Re: lost in my data structure.
by ivancho (Hermit) on Nov 15, 2005 at 04:14 UTC
    The more pressing question is whether you need such a deep structure.. Whenever I see something like
    } } } } };
    I generally think there's something wrong with the data structure.. As you already found out, iterating over the leaves is a nightmare - also, you'd better be very sure that the categories you divided things into (with some generic hash keys) are the only ways you'd be looking for them. Otherwise, any other search turns into an ugly (hardcoded) mixture of qw/grep map keys values/
    Instead, you could consider adding some redundancy in exchange for some clarity/flexibiltiy..
    For example, you could have an AoH, say, something like
    my @ports = ( { id => 0, ip => "ip1", protocol => "tcp", port => 21, service => "ftp", state => "open", }, { id => 1, ... } );
    So, this will force you to repeat "ip1" and "ip2" and "tcp" a few times more than you need to - but you will have several degrees of freedom more than the original structure above, you will be able to iterate through all of your ports immediately, to search on any criteria and in general you will have a better idea of how a port is represented inside your structure - ie, it's just the next element of the array, rather than a leaf in some branching tree..
    you can then find, say, all open ports:
    my @open_ports = grep {$_->{state} eq "open"} @ports;
    It's a sort of OO approach, and I think it adds a lot of flexibility here..
    Update expand on why a 5-layer hash is not the best thing since sliced bread...

      Hello dear Monks,

      first, thank you all, you guys are great.

      - GrandFather : since one long debbugging session (after I added warnings when my script was already finished) I now ALWAYS start my scripts with strict and warnings. But it's never said enough !

      - graff && ivancho : that's because it's only a small part (but the 'deepest') of the data. Beside 'tcp' is 'udp', and then a lot of other infos

      The goal of all this is to store everything in one db file, and then to check this against a previous snapshot

      I thought this way I could loop through the 'snapshot hash' and check for new keys and modified values

      But maybe it's not the best way ?

      Have a wonderful day

Re: lost in my data structure.
by GrandFather (Sage) on Nov 15, 2005 at 03:55 UTC

    You might like to use strict; use warnings; with that and fix the syntax first. Note that because you are using hashes the order of the keys is not preserved unless you use tie magic. The following gets close to what you would like to see:

    use strict; use warnings; my %host = ( 'ip1' => { 'tcp' => { '21' => { 'state' => 'open', 'service' => 'ftp', }, '80' => { 'state' => 'open', 'service' => 'web', } } }, 'ip2' => { 'tcp' => { '23' => { 'state' => 'open', 'service' => 'telnet', }, '80' => { 'state' => 'open', 'service' => 'web', } } } ); for (keys %host) { my %ip = %{$host{$_}{'tcp'}}; print "$_\n"; for my $port (keys %ip) { print " $port " . join ' ', map {$ip{$port}{$_}} keys %{$ip{$por +t}}; print "\n"; } }

    prints:

    ip1 21 ftp open 80 web open ip2 23 telnet open 80 web open

    Perl is Huffman encoded by design.
Re: lost in my data structure.
by graff (Chancellor) on Nov 15, 2005 at 03:55 UTC
    If you never have anything besides "tcp" under "ip1" (or "ip2", etc), then "tcp" represents an unnecessary layer in the structure -- based on the sample data you show, the top-level keys could just as well be "ip1.tcp", "ip2.tcp", etc.

    Anyway, something like this would produce the output format you suggested:

    for my $ip ( sort keys %host ) { print "$ip\n"; for my $proto ( sort keys %{$host{$ip}} ) { # assuming there will +be keys other than "tcp" for my $port ( sort {$a<=>$b} keys %{$host{$ip}{$proto}} ) { printf( "%3d\t%s\t%s\n", $port, $host{$ip}{$proto}{$port}{state}, $host{$ip}{$proto}{$port}{service} ); } } }
    Note how the curly braces are placed around  $host{$ip} and then also around  $host{$ip}{$proto} so that these hash values (which happen to be a references to another hash) can be dereferenced for use with the "keys" function -- the "%" outside of those exta curlies tells perl to pass the entire dereferenced hash to "keys".
Re: lost in my data structure.
by garyaj (Initiate) on Nov 15, 2005 at 04:04 UTC
    One way is:
    for my $ip (keys %host) {
      print "$ip\n";
      for my $port (keys %{$host{$ip}{'tcp'}}) {
        print $port,"  ";
        print $host{$ip}{'tcp'}{$port}{'state'},"  ";
        print $host{$ip}{'tcp'}{$port}{'service'},"\n";
      }
    }
    

    As for adding another line, you could do this:

    %host {
       
      'ip1' => {
         'tcp' => {
            '21' => {
               'state' => 'open',
               'service' => 'ftp',
            },
            '80' => {
               'state' => 'open',
               'service' => 'web',
            },
            '53' => {
               'state' => 'open',
               'service' => 'web',
             },
          },
        },
      },
    ...
    

    Be very careful with your punctuation. Hash elements are separated by commas not semicolons and string values must be in quotes.

Re: lost in my data structure.
by hraj (Novice) on Nov 15, 2005 at 04:13 UTC
    Hi,
    Since there are some syntax errors in your Hash array I have modified your Hash array as:
    %host = ( 'ip1' => { 'tcp' => { '21' => { 'state' => 'open', 'service' => 'ftp' }, '80' => { 'state' => 'open', 'service' => 'web' } } }, 'ip2' => { 'tcp' => { '23' => { 'state' => 'open', 'service' => 'telnet' }, '80' => { 'state' => 'open', 'service' => 'web' } } } );
    You can use reference operator '->' to fetch complex datastructures and the code to display as per your requirement is as follows:
    foreach $ip (keys %host) { print $ip . "\n"; foreach $type (keys %{$host{$ip}}) { foreach $no (keys %{$host{$ip}->{$type}}) { print " " . $no . "\t" . $host{$ip}->{$type}->{$no}->{'sta +te'} . "\t" . $host{$ip}->{$type}->{$no}->{'service'} . "\n"; } } }
    You can also add 53 open dns to "ip1" as:
    $host{'ip1'}->{'tcp'}->{53}->{'state'} = 'open'; $host{'ip1'}->{'tcp'}->{53}->{'service'} = 'dns';
    Good Luck,
    Hraj

    Edited by planetscape - replaced <pre> with <code> tags

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://508466]
Approved by graff
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (1)
As of 2020-10-25 19:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My favourite web site is:












    Results (249 votes). Check out past polls.

    Notices?