deep
deep

Reputation: 716

Perl deferencing from Hash of Array Structure

I have a hash of array and hash which looks like follow:

$VAR1 = \{
           'abc' => {
                       'def' => 'WorkSet',
                       'products' => [
                                       {
                                         'prodtype' => 'Dell',
                                         'product' => 'Powerconnect-5600'
                                       },
                                       {
                                         'prodtype' => 'Dell',
                                         'product' => 'R-720'
                                       },
                                       {
                                         'prodtype' => 'Dell',
                                         'product' => 'R-920'
                                       }
                                     ]
                     },
        '123' => {
                        '456' => 'WorkSet',
                        'products' => [
                                        {
                                          'prodtype' => 'Dell',
                                          'product' => '210'
                                        },
                                        {
                                          'prodtype' => 'Dell',
                                          'product' => 'TZ-200'
                                        },
                                        {
                                          'prodtype' => 'Dell',
                                          'product' => 'TZ-200'
                                        },
                                       ]
          }
 }

I would like to have such that:

  Branch: Workset
  Build Number: abc
  product : Dell producttype : PowerConnect-5600
  product : Dell producttypr : R-720
  product : Dell producttype : R-920

It should be same for the hash value 123 too.

I know to deference the above hash individually, but finding it difficult to do it in a loop.

Please give me your guidance.

FYI, I have listed the above hash using Data:Dumper perl module.

        This is what I thought and tried but not getting the required answer in loops:

  my @unique = uniq @version;
  foreach $version(@ unique){
  my $i=0;
  print "$list->{$version}{branch}\n";
  print "$list->{$version}->{products}->[$i]->{product}\n";
  $i=$i+1;
  } where @unique = qw (abc,123)

Upvotes: 0

Views: 195

Answers (4)

deep
deep

Reputation: 716

This works too:

    while (my ($build_num, $href) = each %$list) {
    my ($branch, $aref, $username);
    $aref = $href->{'products'};
    $branch = $href->{branch};
    $username = $href->{user};

    for my $href (@$aref) {
     print "product: $href->{prodtype}     prodtype: $href->{product}       qbs_id:$href->{qbs_id}\n";
   }

Just edited @Chris answer with some simple modifications without using ref..Thank you

Upvotes: 0

David W.
David W.

Reputation: 107040

The question has already been answered and accepted, but I have to open my trap anyway...

First, I think your structure is off. It should look like this:

$VAR1 = {
          '123' => bless( {
                            'ARRAY' => [
                                         bless( {
                                                  'PRODUCT' => 'Dell',
                                                  'TYPE' => '210'
                                                }, 'Product' ),
                                         bless( {
                                                  'PRODUCT' => 'Dell',
                                                  'TYPE' => 'TZ-200'
                                                }, 'Product' ),
                                         bless( {
                                                  'PRODUCT' => 'Dell',
                                                  'TYPE' => 'TZ-200'
                                                }, 'Product' )
                                       ]
                          }, 'Build' ),
          'abc' => bless( {
                            'ARRAY' => [
                                         bless( {
                                                  'PRODUCT' => 'Dell',
                                                  'TYPE' => 'Powerconnect-5600'
                                                }, 'Product' ),
                                         bless( {
                                                  'PRODUCT' => 'Dell',
                                                  'TYPE' => 'R-720'
                                                }, 'Product' ),
                                         bless( {
                                                  'PRODUCT' => 'Dell',
                                                  'TYPE' => 'R-920'
                                                }, 'Product' )
                                       ]
                          }, 'Build' )
        };

I base this on the output you want. It looks like you have Builds that consist of a list of products. The products consist of a Product Type and the Product Description. In your original structure you have:

 `456` => Workset`

But this part of the structure makes no sense, and you don't use it. Maybe you meant this to be the Branch Type? It's hard to say. Let everything else I don't understand, I ignored it.

Now, how should you parse this structure? You should use Perl Object Oriented Programming. This allows you to stop worrying about your data structure. Your class definitions will take care of that. You don't have to worry if this was an array of hashes, or a hash or arrays, or maybe an array or arrays, of hashes.

Here's the entire main program. Two loops: In the first loop, I read in the data (consisting of three tab separated items: The Build Number, Prodtype, and Product). It's clear, short, and easy to understand. It takes just six lines of code to parse through your entire data structure:

use strict;
use warnings;
use feature qw(say);

my %workset;
my $prev_build;

#
# Read in the Data
#
while ( my $line = <DATA> ) {
    chomp $line;
    my ($build_num, $prodtype, $description) = split /\t/, $line;
    my $product = Product->new( $prodtype, $description );
    if ( not $prev_build or $prev_build ne $build_num ) {
        $workset{$build_num} = Build->new;
        $prev_build = $build_num;
    }
    $workset{$build_num}->Push($product);
}

#
# Parse the structure and Print it out
#
for my $workset ( sort keys %workset ) {
    say "Build Number: " . $workset;
    while (  my $product = $workset{$workset}->Pop ) {
        say "Product: " . $product->Product . " Prodtype: " . $product->Type;
    }
}

And here's the output:

Build Number: 123
Product: Dell Prodtype: TZ-200
Product: Dell Prodtype: TZ-200
Product: Dell Prodtype: 210
Build Number: abc
Product: Dell Prodtype: R-920
Product: Dell Prodtype: R-720
Product: Dell Prodtype: Powerconnect-5600

Just like you wanted.

The rest of my program is the class definitions. Most of the time, I simply append the definitions to the end of my main program because I never use them again. Still, using object oriented code makes programming faster and easier.

And here's the rest of my program:

package Product;

sub new {
    my $class   = shift;
    my $product = shift;
    my $type        = shift;

    my $self = {};
    bless $self, $class;

    $self->Product($product);
    $self->Type($type);

    return $self;
}

sub Product {
    my $self        = shift;
    my $product = shift;

    if ( defined $product ) {
        $self->{PRODUCT} = $product;
    }
    return $self->{PRODUCT};
}

sub Type {
    my $self = shift;
    my $type = shift;

    if ( defined $type ) {
        $self->{TYPE} = $type;
    }
    return $self->{TYPE};
}

package Build;
use Carp;

sub new {
    my $class = shift;

    my $self = {};
    bless $self, $class;

    return $self;
}

# PRIVATE -- No peeking
sub _Array_ref {
    my $self = shift;

    if ( not exists $self->{ARRAY} ) {
        $self->{ARRAY} = [];
    }
    return $self->{ARRAY};
}

sub Index {
    my $self  = shift;
    my $index = shift;

    if ( not defined $self->{INDEX} ) {
        $self->{INDEX} = 0;
    }

    if ( defined $index ) {
        $self->{INDEX} = $index;
    }

    if ( $self->{INDEX} < 0 or $self->{INDEX} > $self->Size ) {
        croak qq(Index out of range: Set to "$index");
    }

    return $self->{INDEX};
}

sub Array {
    my $self = shift;
    my @array = @{ $self->_Array_ref };

    return wantarray ? @array : \@array;
}

sub Size {
    my $self = shift;

    my $array_ref = $self->_Array_ref;
    return $#{ $array_ref };
}

sub Push {
    my $self    = shift;
    my $product = shift;

    if ( not defined $product or not $product->isa("Product") )  {
        croak qq(Push Method for requires a Product Class to push);
    }

    my $array_ref = $self->_Array_ref;
    push @{ $array_ref }, $product;

    return $#{ $array_ref };
}

sub Pop {
    my $self = shift;

    my $array_ref = $self->_Array_ref;
    return pop @{ $array_ref };
}

sub Next {
    my $self = shift;

    my $index = $self->Index;
    my $array_ref = $self->_Array_ref;
    $index += 1;
    $self->Index($index);

    return ${ $array_ref }[$index];
}

sub Prev {
    my $self = shift;

    my $index = $self->Index;
    my $array_ref = $self->_Array_ref;
    $index -= 1;
    $self->Index($index);

    return ${ $array_ref }[$index];
}

package main;

__DATA__
abc Dell    Powerconnect-5600
abc Dell    R-720
abc Dell    R-920
123 Dell    210
123 Dell    TZ-200
123 Dell    TZ-200

Upvotes: 1

hwnd
hwnd

Reputation: 70722

However, I realize this is not exactly your data structure. I am providing an example of a way you could slightly go about a different approach.

use strict;
use warnings;
use feature 'say';

my $h = {
      abc => {
          Workset => { 
                       Dell => [ 
                                 'Powerconnect-5600', 
                                 'R-270', 
                                 'R-920', 
                               ],
                     },
             },
      123 => {
          Workset => {
                       Dell => [
                                 '210',
                                 'TZ-200',
                               ],
                     },
             },
};

for my $k ( keys %$h ) {
   for my $j ( keys %$h->{$k} ) {
      for my $prod ( keys %$h->{$k}{$j} ) {
         say "Branch: $j";
         say "Build Number: $k";
         say "product : $prod producttype : " . $_ for @{ $h->{$k}{$j}{$prod} };
      }
   }
}

Output:

Branch: Workset
Build Number: 123
product : Dell producttype : 210
product : Dell producttype : TZ-200

Branch: Workset
Build Number: abc
product : Dell producttype : Powerconnect-5600
product : Dell producttype : R-270
product : Dell producttype : R-920

Upvotes: 1

Chris Charley
Chris Charley

Reputation: 6573

This will dereference your structure (provided you have just 2 elements for each build number - the branch and the array or products). You have 'product' as 'prodtype', (eg prodtype => Dell), in your hash and 'prodtype' as 'product' - reverse of names. You should consider improving your data structure as suggested by Jason Gray.

#!/usr/bin/perl
use strict;
use warnings;

my $hash = {
           'abc' => {
                       'def' => 'WorkSet',
                       'products' => [
                                       {
                                         'prodtype' => 'Dell',
                                         'product' => 'Powerconnect-5600'
                                       },
                                       {
                                         'prodtype' => 'Dell',
                                         'product' => 'R-720'
                                       },
                                       {
                                         'prodtype' => 'Dell',
                                         'product' => 'R-920'
                                       }
                                     ]
                     },
        '123' => {
                        '456' => 'WorkSet',
                        'products' => [
                                        {
                                          'prodtype' => 'Dell',
                                          'product' => '210'
                                        },
                                        {
                                          'prodtype' => 'Dell',
                                          'product' => 'TZ-200'
                                        },
                                        {
                                          'prodtype' => 'Dell',
                                          'product' => 'TZ-200'
                                        },
                                       ]
          }
};

while (my ($build_num, $href) = each %$hash) {
    my ($branch, $aref);
    for my $key (keys %$href) {
        if (ref($href->{$key}) eq 'ARRAY') {
            $aref = $href->{$key};  
        }
        else {
            $branch = $href->{$key};    
        }
    }
    print "Branch: $branch\n";
    print "Build Number: $build_num\n";

    for my $href (@$aref) {
        print "product: $href->{prodtype} prodtype: $href->{product}\n";
    }
    print "\n";
}

This prints output as:

Branch: WorkSet
Build Number: 123
product: Dell prodtype: 210
product: Dell prodtype: TZ-200
product: Dell prodtype: TZ-200

Branch: WorkSet
Build Number: abc
product: Dell prodtype: Powerconnect-5600
product: Dell prodtype: R-720
product: Dell prodtype: R-920

Upvotes: 1

Related Questions