complex data structure

Discussion in 'Perl Misc' started by Jeff, Jun 26, 2004.

  1. Jeff

    Jeff Guest

    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
     
    Jeff, Jun 26, 2004
    #1
    1. Advertising

  2. Jeff

    Bob Walton Guest

    Jeff wrote:

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

    --
    Bob Walton
    Email: http://bwalton.com/cgi-bin/emailbob.pl
     
    Bob Walton, Jun 26, 2004
    #2
    1. Advertising

  3. Jeff <> wrote:
    > I've created a beast! Here is my data structure:

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


    The problem is that $hash{items}[0] is a reference to an array reference.
    This is due to the backslashed brace ( \[ ). Why do you do that?

    %hash=(simpsons=>{items=>[\[{haha=>'nelsonmunce'}],\[{whoohoo=>'homersimpson'}]]});
    $homer = ${ $hash{simpsons}{items}[1] }->[0]{whoohoo};

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


    Why are you mixing 'for' and 'foreach'?

    foreach $key (keys %hash) {
    foreach $ref (@{$hash{$key}{$items}}) {
    foreach $who (@$$ref) {
    # ^^^^^^
    foreach $item (keys %$who) {
    print "$item => $who->{$item}\n";
    }
    }
    }
    }

    --
    Glenn Jackman
    NCF Sysadmin
     
    Glenn Jackman, Jun 26, 2004
    #3
  4. Jeff

    Jeff Guest

    Bob Walton wrote:
    > Jeff wrote:
    >
    >> 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
     
    Jeff, Jun 26, 2004
    #4
  5. > 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
     
    Sherm Pendley, Jun 26, 2004
    #5
  6. Jeff

    Bob Walton Guest

    Jeff wrote:

    > Bob Walton wrote:
    >
    >> Jeff wrote:

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


    > Jeff


    --
    Bob Walton
    Email: http://bwalton.com/cgi-bin/emailbob.pl
     
    Bob Walton, Jun 26, 2004
    #6
  7. Jeff

    Jeff Guest

    Bob Walton wrote:
    > Jeff wrote:
    >
    >> Bob Walton wrote:
    >>
    >>> Jeff wrote:

    >
    > ...
    >
    >> 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;
    }
     
    Jeff, Jun 26, 2004
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jeff

    complex data structure

    Jeff, Jun 26, 2004, in forum: Perl
    Replies:
    5
    Views:
    515
  2. Waqqas Farooq

    complex data structure in httpcookie

    Waqqas Farooq, Jun 30, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    345
    avnrao
    Jun 30, 2004
  3. spar
    Replies:
    4
    Views:
    320
    Alex Martelli
    Oct 11, 2004
  4. Sigmathaar
    Replies:
    3
    Views:
    326
    Bob Hairgrove
    Dec 6, 2005
  5. Ram
    Replies:
    3
    Views:
    428
    Barry Schwarz
    Mar 24, 2009
Loading...

Share This Page