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

Hello Monks, Apologies if this has been asked before, but I am having trouble deciding on the best way to represent a certain data structure in perl.
Basically I want to store the layout of our network (read from a text file generated by another script) It looks something like this:
Main Switch Port
01/01 -> router
01/01 -> server1
01/01 -> hpswitch1 -> port1 -> workstation1
01/01 -> hpswitch1 -> port2 -> workstation2
01/01 -> hpswitch1 -> port3 -> workstation3
01/02 -> hpswitch2 -> port1 -> server 4
01/02 -> hpswitch2 -> port2 -> server5
01/03 -> server2
02/01 -> server3
02/02 -> hpswitch3 -> port1 -> workstation4
02/02 -> hpswitch3 -> port2 -> workstation5
etc...

So the entries like 01/01 are the main switch blade and port numbers, and hanging off those are other servers, or other switches with more ports and more things hanging off them. At the moment I am representing the data like this

For items that aren't switches
%network{$bladeAandPort}{$item} = $host

e.g.
01/01 -> item1 = router
01/01 -> item2 = server1

and for the switches I am nesting another hash inside like this...
%network{$bladeAandPort}{$item}{$switch}{$switchport}

e.g.
01/01 -> item3 -> hpswitch1 -> port01 = workstation1

However, when I come to iterate through all these, I need a way to say:

If the 3rd item in the data structure is a string, then it's a machine name, otherwise it's a ref to another hash, i'e it's a switch. So how do I determine if a value of a hash key is a string or a ref to another hash?

Additionally, is this the best way to represent this kind of stuff. This data map will be changing quite regularly, so I will have to remake the map adding or delete hashes as things get moved around.

The aim of this code will be to compare a stored version of this data with the current version so we get notified of any changes made to the network, E.g. "workstation1 has been unplugged from hpswitch1."

Am I going about this the best way? Hope this is kind of clear, any help greatly appreciated

Nick

Replies are listed 'Best First'.
Re: generating hashes of hashes
by jwkrahn (Abbot) on Aug 27, 2008 at 17:46 UTC

    Use the ref function to determine if a scalar contains a reference to a hash.

Re: generating hashes of hashes
by ikegami (Patriarch) on Aug 27, 2008 at 17:50 UTC

    So how do I determine if a value of a hash key is a string or a ref to another hash?

    ref

    is this the best way to represent this kind of stuff.

    Looks pretty good. Objects are an alternative, but not necessarily better.

    The aim of this code will be to compare a stored version of this data with the current version so we get notified of any changes made to the network, E.g. "workstation1 has been unplugged from hpswitch1."

    A 2D array for the blade and port might make things slightly easier.

Re: generating hashes of hashes
by jethro (Monsignor) on Aug 27, 2008 at 17:59 UTC

    To find out if a value is a reference, just use ref()

    One could store this data structure as a tree (and there are modules that help with this) but for your task a simple hash might be all you need. Just store for every machine and switch the switch and port it is connected to. So $connect{workstation5}='hpswitch3:2'; and $connect{hpswitch1}='Main Switch:01/01';

    Advantages: No links to keep consistent, easy to write to disk, simple traversing. Disadvantage: Will get slow to ask more complex questions like 'which machines are connected below Main Switch 01/01?', if your machine pool gets above 1000 and you need to ask those questions a lot. But if all you want to do is note all changes, then this structure is as fast as anything

    UPDATE: Small rewording of the disadvantage
Re: generating hashes of hashes
by wfsp (Abbot) on Aug 28, 2008 at 08:03 UTC
    I agree with the other responses, your way looks fine. Sometimes it is worth considering "self documenting data" which can help document your code.

    For example

    next unless $item->{is_a_switch};
    gives you a good indication of what the question is and what sort of answer you can expect.

    As has been pointed out, with this approach you are more than halfway to having an object.

    #!/usr/local/bin/perl use strict; use warnings; my %network = get_network(); for my $blade (keys %network){ for my $item (@{$network{$blade}}){ next unless $item->{is_a_switch}; print qq{$item->{item_name}\n}; for my $kit (@{$item->{more_kit}}){ print qq{ kit: $kit->{kit_name}\n}; } } } sub get_network { my %network = ( 01/01 => [ { is_a_switch => 0, item_name => q{router}, more_kit => 0, }, { is_a_switch => 1, item_name => q{hpswitch1}, more_kit => [ { port => q{port1}, kit_name => q{workstation1}, }, { port => q{port2}, kit_name => q{workstation2}, }, ], } ], 01/02 => [ { is_a_switch => 1, item_name => q{hpswitch2}, more_kit => [ { port => q{port1}, kit_name => q{server4}, }, { port => q{port2}, kit_name => q{server5}, }, ], } ], 01/03 => [ { is_a_switch => 0, item_name => q{server2}, more_kit => 0, }, ], ); return %network; }
    output
    hpswitch1 kit: workstation1 kit: workstation2 hpswitch2 kit: server4 kit: server5
      Great, thanks to all of you for taking the time to respond. This gives me a load of new things to try. Cheers Nick