complex data structure

J

Jeff

I've created a beast! Here is my data structure:

$VAR1 = 'bunkers';
$VAR2 = {
'items' => [
\[
{
'archie' => 'conservative'
}
],
\[
{
'meathead' => 'liberal'
}
]
]
};
$VAR3 = 'simpsons';
$VAR4 = {
'items' => [
\[
{
'haha' => 'nelson munce'
}
],
\[
{
'whoohoo' => 'homer simpson'
}
]
]
};

How do I access the hash key value pairs? This fails at runtime:
(Not a HASH reference at haha line 22.)

foreach $key (keys %hash){
print "[".$key."]\n";
for $who (@{$hash{$key}{items}}){
foreach $item (keys %{$who}){
print $item . " => " . $who->{$item} . "\n";
}
}
}

TIA,
Jeff
 
B

Bob Walton

Jeff said:
I've created a beast! Here is my data structure:

$VAR1 = 'bunkers';
$VAR2 = {
'items' => [
\[
{
'archie' => 'conservative'
}
],
\[
{
'meathead' => 'liberal'
}
]
]
};
$VAR3 = 'simpsons';
$VAR4 = {
'items' => [
\[
{
'haha' => 'nelson munce'
}
],
\[
{
'whoohoo' => 'homer simpson'
}
]
]
};

How do I access the hash key value pairs? This fails at runtime:
(Not a HASH reference at haha line 22.)

foreach $key (keys %hash){
print "[".$key."]\n";
for $who (@{$hash{$key}{items}}){
foreach $item (keys %{$who}){
print $item . " => " . $who->{$item} . "\n";
}
}
} ....
Jeff

That *is* a beast of a structure. I'll take your word for it that that
is really the structure you want (scalar references to array references
included). I also assume that your *original* data structure was a hash
containing $VAR1 through $VAR4 as keys/values, and that you didn't
properly dump a reference to that outside hash, but rather independently
dumped the keys/values to that hash. If so, here you go...

use warnings; #let Perl help you all it can
use strict; #let Perl help you all it can
my $VAR1 = 'bunkers';
my $VAR2 = {
'items' => [
\[
{
'archie' => 'conservative'
}
],
\[
{
'meathead' => 'liberal'
}
]
]
};
my $VAR3 = 'simpsons';
my $VAR4 = {
'items' => [
\[
{
'haha' => 'nelson munce'
}
],
\[
{
'whoohoo' => 'homer simpson'
}
]
]
};
#reconstitute assumed original structure:
my %hash=($VAR1,$VAR2,$VAR3,$VAR4);
=item
How do I access the hash key value pairs? This fails at runtime:
(Not a HASH reference at haha line 22.)
=cut
foreach my $key (keys %hash){
print "[".$key."]\n";
for my $who (@{$hash{$key}->{items}}){
foreach my $item (keys %{${$$who}[0]}){
print $item . " => " . $$who->[0]->{$item} . "\n";
}
}
}

HTH. You didn't show enough of your intended data structure for me to
know if you actually intend to have one-element arrays where I show them
in the code -- if they will actually have more than one element, you
will need to introduce another foreach loop to iterate over them. My
guess this isn't the data structure you really want.
 
J

Jeff

Bob said:
Jeff said:
I've created a beast! Here is my data structure:

$VAR1 = 'bunkers';
$VAR2 = {
'items' => [
\[
{
'archie' => 'conservative'
}
],
\[
{
'meathead' => 'liberal'
}
]
]
};
$VAR3 = 'simpsons';
$VAR4 = {
'items' => [
\[
{
'haha' => 'nelson munce'
}
],
\[
{
'whoohoo' => 'homer simpson'
}
]
]
};

How do I access the hash key value pairs? This fails at runtime:
(Not a HASH reference at haha line 22.)

foreach $key (keys %hash){
print "[".$key."]\n";
for $who (@{$hash{$key}{items}}){
foreach $item (keys %{$who}){
print $item . " => " . $who->{$item} . "\n";
}
}
}
...

Jeff


That *is* a beast of a structure. I'll take your word for it that that
is really the structure you want (scalar references to array references
included). I also assume that your *original* data structure was a hash
containing $VAR1 through $VAR4 as keys/values, and that you didn't
properly dump a reference to that outside hash, but rather independently
dumped the keys/values to that hash. If so, here you go...

use warnings; #let Perl help you all it can
use strict; #let Perl help you all it can
my $VAR1 = 'bunkers';
my $VAR2 = {
'items' => [
\[
{
'archie' => 'conservative'
}
],
\[
{
'meathead' => 'liberal'
}
]
]
};
my $VAR3 = 'simpsons';
my $VAR4 = {
'items' => [
\[
{
'haha' => 'nelson munce'
}
],
\[
{
'whoohoo' => 'homer simpson'
}
]
]
};
#reconstitute assumed original structure:
my %hash=($VAR1,$VAR2,$VAR3,$VAR4);
=item
How do I access the hash key value pairs? This fails at runtime:
(Not a HASH reference at haha line 22.)
=cut
foreach my $key (keys %hash){
print "[".$key."]\n";
for my $who (@{$hash{$key}->{items}}){
foreach my $item (keys %{${$$who}[0]}){
print $item . " => " . $$who->[0]->{$item} . "\n";
}
}
}

HTH. You didn't show enough of your intended data structure for me to
know if you actually intend to have one-element arrays where I show them
in the code -- if they will actually have more than one element, you
will need to introduce another foreach loop to iterate over them. My
guess this isn't the data structure you really want.

I'm certainly open to suggestions. Here is the method that creates the
hash:

sub getHashes()
{
my $this = shift;
my ($sep,$bad_programmer) = @_;
my (%hash, $hash);
my $lines = "";
my (@list, @cols);
my ($left,$right);
my ($prefix, $items);

if(open(FILE, "<" . $this->{"file"})){
flock(FILE, $LOCK_EX);
while(<FILE>){
next if /^$/;
next if /^\s*#/;
$lines .= $_;
}
flock(FILE, $LOCK_UN);
close(FILE);
}

$prefix = 'default';
$items = 'items';
foreach my $thing ( split( /\n/, $lines ) ){
if($thing =~ m/^\[([^\]]+)\]$/){
$prefix = $1;
print "PREFIX: $prefix\n";
next;
}
($left,$right) = split( /$sep/, $thing );
# Trim begnining and trailing whitespace
$left=~s/^\s+//;
$right=~s/^\s+//;
$left=~s/\s+$//;
$right=~s/\s+$//;
#$hash{$prefix}{$items} = [{ $left => $right, }];
push(@{$hash{$prefix}{$items}}, \[{ $left => $right, }]);
}
return %hash;
}

I had really wanted to push arrays (not refs) on $hash{$prefix}{$items}

Jeff
 
S

Sherm Pendley

foreach $item (keys %{$who}){

The major problem in the above is that $who is neither a Simpsons reference
nor a Bunkers reference, it's a Dr. Seuss reference.

Okay, seriously... Like the error says, $who is not a hash ref, it's an
array ref, so the above should be:

foreach $item (@{$who}) {

Also, the array referred to by $who contains two items - each is a reference
to a reference (not a typo - note the backslash in the Data::Dumper output)
to an array. The inner array contains a single item, which is a reference
to a hash containing a single item.

To work with the code as given, your data structure would need to look in
part like this:

$VAR4 = {
'items' => {
'haha' => 'nelson munce',
'whoohoo' => 'homer simpson'
}
};

If the data is correct as given, then the code will need to be restructured
to correctly deal with the additional layers of array references.

To help deal with these issues, I suggest reading 'perldoc
perlreftut' (References Tutorial), 'perldoc perldsc' (Data Structures
Cookbook), and 'perldoc perllol' (Lists of Lists), in that order.

sherm--
 
B

Bob Walton

Jeff said:
....
I'm certainly open to suggestions. Here is the method that creates the
hash:

sub getHashes()
{
my $this = shift;
my ($sep,$bad_programmer) = @_;
my (%hash, $hash);
my $lines = "";
my (@list, @cols);
my ($left,$right);
my ($prefix, $items);

if(open(FILE, "<" . $this->{"file"})){
flock(FILE, $LOCK_EX);
while(<FILE>){
next if /^$/;
next if /^\s*#/;
$lines .= $_;
}
flock(FILE, $LOCK_UN);
close(FILE);
}


else { #you don't really want to continue if the open failed
die "file open failed for $this->{file}"
}

$prefix = 'default';
$items = 'items';
foreach my $thing ( split( /\n/, $lines ) ){
if($thing =~ m/^\[([^\]]+)\]$/){
$prefix = $1;
print "PREFIX: $prefix\n";
next;
}
($left,$right) = split( /$sep/, $thing );
# Trim begnining and trailing whitespace
$left=~s/^\s+//;
$right=~s/^\s+//;
$left=~s/\s+$//;
$right=~s/\s+$//;
#$hash{$prefix}{$items} = [{ $left => $right, }];


In the above (if it weren't commented out), you would be storing a
reference to a one element anonymous array, the contents of which are a
reference to an anonymous one-key hash. If you just have two things to
store, just put them in the array: = [$left,$right]; It just garbages
things up to have one-element arrays and one-key hashes.

push(@{$hash{$prefix}{$items}}, \[{ $left => $right, }]);


Here, you are pushing a reference to a reference to a one-element
anonymous array, the element of which is a reference to a one-key
anonymous hash. That's *way* out of control: [untested]

push(@{$hash{$prefix}{%items}},[$left,$right];

should accomplish the same data storage task, but in a much cleaner fashion.

Note that

$q=[1,2,3,4];

stores a reference to an anonymous 4-element array into scalar variable
$q. It is not necessary or desirable to do:

$q=\[1,2,3,4];

which stores a reference to a reference to an anonymous 4-element array
in $q.

}
return %hash;
}

I had really wanted to push arrays (not refs) on $hash{$prefix}{$items}


I'm not sure what your are trying to say in that statement. A hash
element (or an array element for that matter) can only hold one sort of
thing: a scalar. Therefore, you can't "push arrays on $hash{...}" --
but you *can* place a *reference* to an array in a hash as a hash
element value.

Check out:

perldoc perldsc
perldoc perllol
perldoc perlref

etc.
 
J

Jeff

Bob said:
Jeff said:
...

I'm certainly open to suggestions. Here is the method that creates the
hash:

sub getHashes()
{
my $this = shift;
my ($sep,$bad_programmer) = @_;
my (%hash, $hash);
my $lines = "";
my (@list, @cols);
my ($left,$right);
my ($prefix, $items);

if(open(FILE, "<" . $this->{"file"})){
flock(FILE, $LOCK_EX);
while(<FILE>){
next if /^$/;
next if /^\s*#/;
$lines .= $_;
}
flock(FILE, $LOCK_UN);
close(FILE);
}



else { #you don't really want to continue if the open failed
die "file open failed for $this->{file}"
}

$prefix = 'default';
$items = 'items';
foreach my $thing ( split( /\n/, $lines ) ){
if($thing =~ m/^\[([^\]]+)\]$/){
$prefix = $1;
print "PREFIX: $prefix\n";
next;
}
($left,$right) = split( /$sep/, $thing );
# Trim begnining and trailing whitespace
$left=~s/^\s+//;
$right=~s/^\s+//;
$left=~s/\s+$//;
$right=~s/\s+$//;
#$hash{$prefix}{$items} = [{ $left => $right, }];



In the above (if it weren't commented out), you would be storing a
reference to a one element anonymous array, the contents of which are a
reference to an anonymous one-key hash. If you just have two things to
store, just put them in the array: = [$left,$right]; It just garbages
things up to have one-element arrays and one-key hashes.

push(@{$hash{$prefix}{$items}}, \[{ $left => $right, }]);



Here, you are pushing a reference to a reference to a one-element
anonymous array, the element of which is a reference to a one-key
anonymous hash. That's *way* out of control: [untested]

push(@{$hash{$prefix}{%items}},[$left,$right];

should accomplish the same data storage task, but in a much cleaner
fashion.

Note that

$q=[1,2,3,4];

stores a reference to an anonymous 4-element array into scalar variable
$q. It is not necessary or desirable to do:

$q=\[1,2,3,4];

which stores a reference to a reference to an anonymous 4-element array
in $q.

}
return %hash;
}

I had really wanted to push arrays (not refs) on $hash{$prefix}{$items}



I'm not sure what your are trying to say in that statement. A hash
element (or an array element for that matter) can only hold one sort of
thing: a scalar. Therefore, you can't "push arrays on $hash{...}" --
but you *can* place a *reference* to an array in a hash as a hash
element value.

Check out:

perldoc perldsc
perldoc perllol
perldoc perlref

etc.

Thanks. That helped considerably. This is closer to what I wanted:

sub getHashes()
{
my $this = shift;
my ($sep,$bad_programmer) = @_;
my (%hash, $rec);
my $lines = "";
my ($prefix);

if(open(FILE, "<" . $this->{"file"})){
flock(FILE, $LOCK_EX);
while(<FILE>){
next if /^$/;
next if /^\s*#/;
$lines .= $_;
}
flock(FILE, $LOCK_UN);
close(FILE);
}

$prefix = 'default';
$rec = {};
$hash{$prefix} = $rec;
foreach my $thing (split(/\n/, $lines)){
$thing=~s/^\s+//; # trim leading white space
$thing=~s/\s+$//; # trim trailing white space
if($thing =~ m/^\[([^\]]+)\]$/){
$prefix = $1;
$rec = {};
$hash{$prefix} = $rec;
next;
} else {
my($key, $value) = split /$sep/, $thing;
$key=~s/^\s+//;
$value=~s/^\s+//;
$key=~s/\s+$//;
$value=~s/\s+$//;
$rec->{$key} = $value;
}
}
return %hash;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top