Connecting Tech Pros Worldwide Forums | Help | Site Map

complex data structure

Jeff
Guest
 
Posts: n/a
#1: Jul 19 '05
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

Bob Walton
Guest
 
Posts: n/a
#2: Jul 19 '05

re: complex data structure


Jeff wrote:
[color=blue]
> 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";
> }
> }
> }[/color]
....[color=blue]
> Jeff[/color]

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.

--
Bob Walton
Email: http://bwalton.com/cgi-bin/emailbob.pl

Jeff
Guest
 
Posts: n/a
#3: Jul 19 '05

re: complex data structure


Bob Walton wrote:[color=blue]
> Jeff wrote:
>[color=green]
>> 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";
>> }
>> }
>> }[/color]
>
> ...
>[color=green]
>> Jeff[/color]
>
>
> 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.
>[/color]

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

Sherm Pendley
Guest
 
Posts: n/a
#4: Jul 19 '05

re: complex data structure


> 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--

--
Cocoa programming in Perl: http://camelbones.sourceforge.net
Hire me! My resume: http://www.dot-app.org
Bob Walton
Guest
 
Posts: n/a
#5: Jul 19 '05

re: complex data structure


Jeff wrote:
[color=blue]
> Bob Walton wrote:
>[color=green]
>> Jeff wrote:[/color][/color]
....[color=blue]
> 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);
> }[/color]


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

[color=blue]
>
> $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, }];[/color]


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.

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


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.

[color=blue]
> }
> return %hash;
> }
>
> I had really wanted to push arrays (not refs) on $hash{$prefix}{$items}[/color]


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.

[color=blue]
> Jeff[/color]

--
Bob Walton
Email: http://bwalton.com/cgi-bin/emailbob.pl

Jeff
Guest
 
Posts: n/a
#6: Jul 19 '05

re: complex data structure


Bob Walton wrote:[color=blue]
> Jeff wrote:
>[color=green]
>> Bob Walton wrote:
>>[color=darkred]
>>> Jeff wrote:[/color][/color]
>
> ...
>[color=green]
>> 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);
>> }[/color]
>
>
>
> else { #you don't really want to continue if the open failed
> die "file open failed for $this->{file}"
> }
>
>[color=green]
>>
>> $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, }];[/color]
>
>
>
> 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.
>
>[color=green]
>> push(@{$hash{$prefix}{$items}}, \[{ $left => $right, }]);[/color]
>
>
>
> 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.
>
>[color=green]
>> }
>> return %hash;
>> }
>>
>> I had really wanted to push arrays (not refs) on $hash{$prefix}{$items}[/color]
>
>
>
> 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.
>
>[/color]

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;
}



Closed Thread