Plexus
Plexus

Reputation: 77

object oriented perl, set hash values

I have a class:

sub new {

    my ($class, $name) = @_;

    my $self = {        
        _ids => [],
        _devices => {}       
    };

    bless ($self, $class);

    return $self;

}

I pass the subroutine an array, and say:

sub Importids {

    my ($self, $id_ref) = @_;

    foreach $id (@{$id_ref})
    {        
        push(@{$self->{_ids}}, $id);            
    }

}

I further would like to add into this function that I initialize the hash, but am having a hell of a time doing this. In the end I want that my hash looks like this for a number of ids:

_device --> id1|
                --> status --> 0/1
            id2|
                --> status --> 0/1

where the id is the key.

I tried to do this in the function as follows:

sub Importids {

    my ($self, $id_ref) = @_;

    foreach $id (@{$id_ref})
    {    
        push(@{$self->{_ids}}, $id);        
    }

    foreach my $id_value(@{$self->{_ids}})
    {
        $self->{_devices}{$id_value}{'status'} = 0;
    }

}

but when I go to check the contents as follows, it returns only the hex dump of the hashes

for my $hash_key (keys %{$self->{_devices}})
{
    print $self->{_devices}{$hash_key};
    #print keys % {$self->_devices}};
}

gives:

HASH(0x....)
HASH(0x....)
...
...
HASH(0x....)

however, when I try:

for my $hash_key (keys %{$self->{_devices}})
{
    print $self->{_devices}->{$hash_key}->{'status'};

}

I get what I want:

0
0
...
0

How should I access the keys and also add an additional field such as status2''?

Upvotes: 1

Views: 821

Answers (1)

zdim
zdim

Reputation: 66899

Note   The short first part refers to the original question, which changed in the meanwhile.


When you are setting up the hash, you iterate over the array with id's, using $id_value as the variable. Then you assign it to a non-existent key

$self->{_devices}{ids} = $id_value;  # what is 'ids' string?

Thus a key 'ids' will be added and each assignment will overwrite the previous. Also, note that this way the $id_value (would) become values, not keys, contrary to what the question states.

What is supposed to be in $self->{_devices} ? If keys are id's, to be "initialized", say to 0

foreach my $id (@$id_ref)
{
    push @{$self->{_ids}}, $id;
    $self->{_devices}{$id} = 0;
}

There are more compact and clearer ways but let's first clarify if this is intended.


Update to clarification in comments

The objective is to construct a structure

_device --> id1 --> status --> 0/1
            id2 --> status --> 0/1
            ...

We also want to be able to read/change values, and add another kind of 'status'. Let us first forget about classes and build this data structure. See tutorial perlreftut and the data-structures cookbook perldsc.

In Perl we can use a hash reference for this, with further nested hash references

my $hashref = { 
    'id1' => { 
        'status'  => 0,
        'status2' => 0
    }, 
    'id2' => { 
        'status'  => 0,
        'status2' => 0,
    },
    # ...
};

The anonymous hash { status => 0, status2 => 0 } is assigned as a value to keys 'id1', 'id2'. Being a (nameless) reference to a hash it is loosely called hashref (like \%hash is, too). Any reference is a scalar, a single value, so a hashref can be assigned to a key. This is how we build nested (complex) data structures, using references. They are much like pointers.

We can populate it in code like so

use warnings 'all';
use strict;
use Data::Dumper;  # to see our structures

my @ids = qw(id1 id2 id3);

my $hashref;

foreach my $id (@ids) {
    foreach my $stat ('status', 'status2') {
        $hashref->{$id}{$stat} = 0;
    }
}

print Dumper($hashref);

Strictly, we'd need to dereference at each level, $hashref->{$id}->{$stat}, but a notational shortcut allows us to omit all other than the first one (since that then can't mean anything else).

The keys $id are added to the hash once encountered (autovivified). So are the keys $stat in the nested hash(ref) (assigned the value 0 above). We print and change values by

$hashref->{$id}{'status'} = 1;

print "$hashref->{$id}{'status'}\n";

We can add yet another 'status' the same way, $hashref->{$id}{'status3'} = 0.

Now to the class. We use only one 'status' field. Please add suitable error checking.

sub Importids 
{
    my ($self, $id_ref) = @_;

    foreach my $id (@{$id_ref}) 
    {    
        push @{$self->{_ids}}, $id;
        $self->{_devices}{$id}{'status'} = 0;
    }
    return 1;
}

How do we change values for given ids? Let's first come up with an interface. With ids in $id variables and their new values-to-be in $val's, we can imagine a call

$obj->set_status( { $id1 => $val1, $id2 => $val2 } );             
# Or
$obj->set_status( $new_vals_for_ids_hashref );

This builds a hashref inside the call or uses one built previously. Then here is a method for it

sub set_status 
{
    my ($self, $rhstat) = @_;  # check for $rhstat

    foreach my $id (keys %$rhstat) {
        $self->{_devices}{$id} = $rhstat->{$id};
    }
    return 1;
}

This adds new ids if they are not already there, or overwrites the values for the existing ones. We can make this same sub be the getter as well -- when nothing is passed in

sub status 
{
    my ($self, $rstat) = @_; 
    
    return $self->{_devices} if not defined $rstat; 

    foreach my $id (keys %$rstat) {
        $self->{_devices}{$id} = $rstat->{$id};
    }
    return $self->{_devices};
}

with intended uses my $devices = $obj->status(); or with an argument. Since we now return data in one case we do the same when ids are added/changed as well, for interface consistency.

You can add 'status' fields similarly,

$obj->add_status( [ qw(status2 status3) ] );

where [ ... ] is an anonymous array, with a method

sub add_status 
{
    my ($self, $rstats) = @_;

    foreach my $stat (@$rstats) 
    {
        foreach my $id (@{$self->{_ids}}) 
        {
            $self->{_devices}{$id}{$stat} = 0;
        }
    }
    return 1;
}

We can make this method also optionally take and set values for new keys, by passing either a hashref with key-value pairs (add keys and set them), or an arrayref as above, what can be checked in the method (see ref).

Note that there are more compact ways to work with lists of keys/values.

Upvotes: 4

Related Questions