0

Perhaps I have made this more complicated than I need it to be but I am currently trying to store an array that contains, among other things, an array inside a hash in Perl.

i.e. hash -> array -> array

use strict;

my %DEVICE_INFORMATION = {}; #global hash

sub someFunction() {
    my $key = 'name';
    my @storage = ();

    #assume file was properly opened here for the foreach-loop
    foreach my $line (<DATA>) {
        if(conditional) {
            my @ports = ();

            $storage[0] = 'banana';
            $storage[1] = \@ports;
            $storage[2] = '0';

            $DEVICE_INFORMATION{$key} = \@storage;      
        }

        elsif(conditional) {
            push @{$DEVICE_INFORMATION{$key}[1]}, 5;
        }
    }#end foreach
} #end someFunction

This is a simplified version of the code I am writing. I have a subroutine that I call in the main. It parses a very specifically designed file. That file guarantees that the if statement fires before subsequent elsif statement.

I think the push call in the elsif statement is not working properly - i.e. 5 is not being stored in the @ports array that should exist in the @storage array that should be returned when I hash the key into DEVICE_INFORMATION.

In the main I try and print out each element of the @storage array to check that things are running smoothly.

#main execution
&someFunction();

print $DEVICE_INFORMATION{'name'}[0];
print $DEVICE_INFORMATION{'name'}[1];
print $DEVICE_INFORMATION{'name'}[2];

The output for this ends up being... banana ARRAY(blahblah) 0

If I change the print statement for the middle call to:

print @{$DEVICE_INFORMATION{'name'}[1]};

Or to:

print @{$DEVICE_INFORMATION{'name'}[1]}[0];

The output changes to banana [blankspace] 0

Please advise on how I can properly update the @ports array while it is stored inside the @storage array that has been hash'd into DEVICE_INFORMATION and then how I can access the elements of @ports. Many thanks!

P.S. I apologize for the length of this post. It is my first question on stackoverflow.

1
  • Please see the comments under the accepted answer for clarification as to why some of the proper code above was not working correctly. Commented Sep 24, 2015 at 23:41

2 Answers 2

0

I was going to tell you that Data::Dumper can help you sort out Perl data structures, but Data::Dumper can also tell you about your first problem:

Here's what happens when you sign open-curly + close-curly ( '{}' ) to a hash:

use Data::Dumper ();

my %DEVICE_INFORMATION = {}; #global hash
print Dumper->Dump( [ \%DEVICE_INFORMATION ], [ '*DEVICE_INFORMATION ' ] );

Here's the output:

%DEVICE_INFORMATION  = (
                         'HASH(0x3edd2c)' => undef
                       );

What you did is you assigned the stringified hash reference as a key to the list element that comes after it. implied

my %DEVICE_INFORMATION = {} => ();

So Perl assigned it a value of undef.

When you assign to a hash, you assign a list. A literal empty hash is not a list, it's a hash reference. What you wanted to do for an empty hash--and what is totally unnecessary--is this:

my %DEVICE_INFORMATION = ();

And that's unnecessary because it is exactly the same thing as:

my %DEVICE_INFORMATION;

You're declaring a hash, and that statement fully identifies it as a hash. And Perl is not going to guess what you want in it, so it's an empty hash from the get-go.

Finally, my advice on using Data::Dumper. If you started your hash off right, and did the following:

my %DEVICE_INFORMATION; # = {}; #global hash    
my @ports = ( 1, 2, 3 );

# notice that I just skipped the interim structure of @storage
# and assigned it as a literal 
# * Perl has one of the best literal data structure languages out there.
$DEVICE_INFORMATION{name} = [ 'banana', \@ports, '0' ];
print Data::Dumper->Dump( 
      [ \%DEVICE_INFORMATION ]
    , [ '*DEVICE_INFORMATION' ] 
    );

What you see is:

%DEVICE_INFORMATION  = (
                         'name' => [
                                     'banana',
                                     [
                                       1,
                                       2,
                                       3
                                     ],
                                     '0'
                                   ]
                       );

So, you can better see how it's all getting stored, and what levels you have to deference and how to get the information you want out of it.

By the way, Data::Dumper delivers 100% runnable Perl code, and shows you how you can specify the same structure as a literal. One caveat, you would have to declare the variable first, using strict (which you should always use anyway).

Sign up to request clarification or add additional context in comments.

3 Comments

Data::Dumper seems like a power tool. Thank you for bringing that to my attention. Your answer leaves me a bit perplexed, however, as I cannot simply assign @ports in the manner you did. Due to the nature of parsing a file, I will first receive 'banana' and the two 0s. Only after that has been added to DEVICE_INFORMATION will I then get the ports information. Your answer directly assigns ports and includes it in $D_I immediately. I am still left unsure how to dynamically add in an array after I have already hashed some information.
In what may be one of the more embarrassing mistakes I have made, I forgot to update my test file to trigger the "elsif" conditional. Data::Dumper is what brought it to my attention. Thank you kindly Axeman. Your information, and your help, is very much appreciated!
@CaptainRyan: I was just trying to have something in ports to show you what the dump of what you were trying to do would look like. It doesn't demonstrate how to assign the ports, but simulates the case of having a port array.
-1

You update @ports properly.
Your print statement accesses $storage[1] (reference to @ports) in wrong way.
You may use syntax you have used in push.

print $DEVICE_INFORMATION{'name'}[0], ";",
      join( ':', @{$DEVICE_INFORMATION{'name'}[1]}), ";",
      $DEVICE_INFORMATION{'name'}[2], "\n";
print "Number of ports: ", scalar(@{$DEVICE_INFORMATION{'name'}[1]})),"\n";
print "First port: ", $DEVICE_INFORMATION{'name'}[1][0]//'', "\n";
# X//'' -> X or '' if X is undef

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.