#1
  1. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Jul 2003
    Posts
    3,381
    Rep Power
    594

    Passing an array of hashes to a subroutine


    There are lots of examples around for passing arrays to subroutines but I can't seem to get it to work. I am trying to create an array of hashes in a subroutine so I am passing an array reference to it.
    Code:
    .
    .
    .
    my @product;
    loadHTML("product",\@product);
    .
    .
    .
    sub loadHTML {
       my $name=$_[0];
       my @array=@{$_[1]};
    .
    .
    .
       $array[$i++]{
          name=>data[0],
          size=>data[1],
          checksum=>data[2]
       }
    .
    .
    .
    }
    Can someone tell me what I am doing wrong? TIA.
    There are 10 kinds of people in the world. Those that understand binary and those that don't.
  2. #2
  3. !~ /m$/
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    May 2004
    Location
    Reno, NV
    Posts
    4,254
    Rep Power
    1810
    Difficult to say. You aren't using $name, $i isn't discussed, and @data appears out of nowhere.

    From the description it sounds as if you want to take the @product array, turn it into a hash, and push that into a global array. Is that correct?
  4. #3
  5. !~ /m$/
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    May 2004
    Location
    Reno, NV
    Posts
    4,254
    Rep Power
    1810
    I'll just throw some ideas out there:

    Code:
    sub loadHTML {
       my ($name, $ref) = @_;
       my @keys = qw/name size checksum/;
    
       my %hash;
       # fill the hash with data matched to keys in the order provided
       @hash{@keys} = @$ref;
    
       push @global_arrary, \%hash;
    }
    I avoid globals when possible. I'd rather have the @keys here defined globally since they don't need to be redefined over and over. The task is small enough so that a subroutine is probably unnecessary, so that might allow to you reorganize a bit also.

    Edit: Also, I have no idea about $name still; whether it's necessary, or intended to be a key for a hash of hashes, etc.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Jul 2003
    Posts
    3,381
    Rep Power
    594
    $name, $i and $data are really irrelevant to the problem. The are set elsewhere and simply used to set the array element and hash. I just included them to illustrate what I am trying to do. The real issue is a dump of @product outside the subroutine is empty. Whatever $array[$i] is pointing to, is not being set, at least according to Dump(@product).

    The keys are hard coded; 'name' (not to be confused with the variable $name), 'size' and 'checksum'.

    I did not think of producing the hash locally and then pushing it onto the array. I'll give that a try.

    I can't use a global array because this routine sets different arrays. That is why I need to pass it.

    Thanks.
    Last edited by gw1500se; January 10th, 2013 at 10:00 AM.
    There are 10 kinds of people in the world. Those that understand binary and those that don't.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Jul 2003
    Posts
    3,381
    Rep Power
    594
    Pushing a local hash didn't change the result. The problem has to be the way I an referencing the passed array or passing it.
    There are 10 kinds of people in the world. Those that understand binary and those that don't.
  10. #6
  11. !~ /m$/
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    May 2004
    Location
    Reno, NV
    Posts
    4,254
    Rep Power
    1810
    Alright, I think I understand better what you are trying to do.

    Code:
    $array[$i++] = {
          name=>data[0],
          size=>data[1],
          checksum=>data[2]
       };
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Jul 2003
    Posts
    3,381
    Rep Power
    594
    Thanks but no joy. I already found that bit of stupidity. Perhaps if I posted more code it would be helpful.
    Code:
    use strict;
    use Data::Dumper;
    
    my $TOPDIR="/nobackup/testcomp/fbe";
    my @product;
    my @shrinkwrap;
    my @staging;
    
    sub loadHTML {
            my $name=$_[0];
            my @array=@{$_[1]};
            my $fh;
            my @data;
            my $i=0;
            if (-f "$TOPDIR/$name.html") {
                    print("Loading HTML for $name\n");
                    open($fh, "<", "$TOPDIR/$name.html")
                            or die "Failed to open $TOPDIR/$name.html: $!\n";
                    while(<$fh>) {
                            chomp;
                            @data=split(",",$_);
                            $array[$i++]={
                                    name=>$data[0],
                                    size=>$data[1],
                                    checksum=>$data[2]
                            };
                    }
                    close $fh;
            }
            else {
                    print("No HTML found for $name\n");
            }
    }
    
    loadHTML("product",\@product);
    print(Dumper(@product)."\n");
    loadHTML("shrinkwrap",\@shrinkwrap);
    loadHTML("staging",\@staging);
    .
    .
    .
    The only thing printed after the call is \n.
    There are 10 kinds of people in the world. Those that understand binary and those that don't.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    May 2007
    Posts
    765
    Rep Power
    929
    At this point:
    Code:
    my @array=@{$_[1]};
    you made a copy of the @product array and are no longer modifying the the caller's.

    Either change this to:
    Code:
    my $array = $_[1];
    # ...
    $$array[$i++] = { ... };
    Or you could write in more idiomatic perl:
    Code:
    @product = loadHTML( 'product' );
    
    sub loadHTML {
       my @array;
       # ...
       $array[$i++] = { ... };
       # ...
       return @array;
    }
    sub{*{$::{$_}}{CODE}==$_[0]&& print for(%:: )}->(\&Meh);
  16. #9
  17. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Jul 2003
    Posts
    3,381
    Rep Power
    594
    I started with your idiomatic suggestion and could not get that to work (that was many iterations ago so may be worth revisiting). I did not know about the $$ syntax but I can see how that makes sense now that I see it. That fixed it, thanks again.
    There are 10 kinds of people in the world. Those that understand binary and those that don't.
  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Apr 2009
    Posts
    1,930
    Rep Power
    1225
    First, as has already been pointed out, the idiomatic (and better) approach would be to return the data instead of passing in an array ref.

    Personally, based on what you've shown, I'd construct the sub to accept an array of your "html" files and return either an AoA or a HoA.

    I also see an issue with what the code says it's processing vs what it actually is processing. The subroutine name and open call indicate that you're processing html files, however, the while loop clearly shows that you're processing csv files. You really should fix that issue.
  20. #11
  21. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    Jul 2003
    Posts
    3,381
    Rep Power
    594
    Au contrair. The files are technically html files although the content is csv. They are retrieved by other scripts via curl from an httpd server and expected to be in that format.

    As for the idiomatic approach, now that I have it working, I will revisit that approach.

    Thanks.
    There are 10 kinds of people in the world. Those that understand binary and those that don't.

IMN logo majestic logo threadwatch logo seochat tools logo