Access hash of hashes element problem.

Discussion in 'Perl Misc' started by Justin C, May 1, 2007.

  1. Justin C

    Justin C Guest

    I can't seem to access the values in a hash of hashes, but if I iterate
    over it I'm able to print all the values. The error I get if I try to
    access one value is :
    Use of uninitialized value in print at ./test line 20, <PRICES> line 62.

    I've done the best I can in reducing this to the smallest possible code
    that works, it's still quite long, sorry about that.

    ======= START CODE =======
    #!/usr/bin/perl

    use warnings ;
    use strict ;

    my %priceHash ;
    my $zone = 5 ;
    my $weight = 5 ;

    lookupCost() ;

    #for $weight ( keys %priceHash ) {
    # print "$weight : " ;
    # for $zone ( keys %{$priceHash{$weight}} ) {
    # print "\t", $zone, "=", $priceHash{$weight}{$zone}, "\n" ;
    # }
    # print "\n" ;
    #}

    print "Price = ", $priceHash{$weight}{$zone}, "\n" ;

    sub lookupCost {
    open PRICES, "<", "/var/www/inhouse/pforce/pforcePrices.csv"
    or die "Cannot opne prices csv file: $!" ;

    my @lines = <PRICES> ;
    my $header = shift @lines ;
    chomp $header ;
    $header =~ s/\cM//g ; # MS CR/LF
    $header =~ s/zone//ig ;
    $header =~ s/\s+//g ;

    my @zones = split /,/ , $header ;
    shift @zones ; #first one is empty
    foreach ( @lines ) {
    chomp ;
    s/\cM//g ;
    my @prices = split /,/ ;
    my $kg = shift @prices ;

    foreach ( @zones ) {
    $priceHash{$kg}{$_} = shift @prices ;
    }
    }
    }
    ======= CODE END =======

    If I comment the "print" line and uncomment the itteration over the
    hashes it prints, for each weight, the different zones and the prices
    for those zones. It appears to me that all of the information is in the
    hash but I'm not accessing the individual values correctly.

    The .csv, if you want to try this out, can be found at:
    http://81.6.247.208/pforcePrices.csv I didn't paste it here because of
    the size (it's not too big, but it's not appropriate for a usenet post).

    I thank you for any help you can give with this.

    Justin.

    --
    Justin C, by the sea.
     
    Justin C, May 1, 2007
    #1
    1. Advertising

  2. Justin C

    J. Gleixner Guest

    alexamaschoolJustin C wrote:
    > I can't seem to access the values in a hash of hashes, but if I iterate
    > over it I'm able to print all the values. The error I get if I try to
    > access one value is :
    > Use of uninitialized value in print at ./test line 20, <PRICES> line 62.
    >
    > I've done the best I can in reducing this to the smallest possible code
    > that works, it's still quite long, sorry about that.


    It could be smaller. Use __DATA__, for a few lines, of your CSV
    file in your script so that others can run it without having to
    download your CSV file.

    You could use Data::Dumper to verify what's actually in %priceHash.

    use Data::Dumper;
    print Dumper( \%priceHash );
     
    J. Gleixner, May 1, 2007
    #2
    1. Advertising

  3. Justin C

    Mumia W. Guest

    On 05/01/2007 10:39 AM, Justin C wrote:
    > I can't seem to access the values in a hash of hashes, but if I iterate
    > over it I'm able to print all the values. The error I get if I try to
    > access one value is :
    > Use of uninitialized value in print at ./test line 20, <PRICES> line 62.
    > [...]


    Your program is so close to working, but you have one problem--you
    forgot that hash keys are strings and only strings. Where string
    comparisons are concerned, '5' != '5.0'. Change $weight (at the top of
    the program) to "5.0" and rerun your program.

    However, it's better to create the hash keys as simpler numbers, e.g.
    insert 5 rather than 5.0:

    $priceHash{$kg+0}{$_} = shift @prices;

    This causes a warning about "per" which has some function in your data
    that I don't know about.

    I also have two minor nickpicks: "open" is misspelled in the die()
    statement, and the PRICES file is never explicitly closed by program.
    You also do a little more work than is necessary to read in the data.

    This is a shorter version of your program:

    use strict;
    use warnings;
    use File::Slurp;

    my %priceHash;
    my $weight = 5;
    my $zone = 5;

    my @lines = read_file('pforcePrices.csv');
    s/\cM\cJ$// for @lines;

    my $header = shift @lines;
    $header =~ s/(zone|\s+|^,)//ig;
    my @zones = split /,/,$header;

    foreach my $line (@lines) {
    next unless $line =~ /^\d/;
    my @prices = split /,/,$line;
    my $kg = shift @prices;
    foreach my $zn (@zones) {
    $priceHash{$kg+0}{$zn} = shift @prices;
    }
    }

    print "Price: $priceHash{$weight}{$zone}\n";
     
    Mumia W., May 1, 2007
    #3
  4. Justin C <> wrote:
    > I can't seem to access the values in a hash of hashes, but if I iterate
    > over it I'm able to print all the values. The error I get if I try to
    > access one value is :
    > Use of uninitialized value in print at ./test line 20, <PRICES> line 62.


    > I've done the best I can in reducing this to the smallest possible code
    > that works, it's still quite long, sorry about that.


    > ======= START CODE =======
    > #!/usr/bin/perl


    > use warnings ;
    > use strict ;


    > my %priceHash ;
    > my $zone = 5 ;
    > my $weight = 5 ;


    > lookupCost() ;


    > #for $weight ( keys %priceHash ) {
    > # print "$weight : " ;
    > # for $zone ( keys %{$priceHash{$weight}} ) {
    > # print "\t", $zone, "=", $priceHash{$weight}{$zone}, "\n" ;
    > # }
    > # print "\n" ;
    > #}


    > print "Price = ", $priceHash{$weight}{$zone}, "\n" ;


    > sub lookupCost {
    > open PRICES, "<", "/var/www/inhouse/pforce/pforcePrices.csv"
    > or die "Cannot opne prices csv file: $!" ;


    > my @lines = <PRICES> ;
    > my $header = shift @lines ;
    > chomp $header ;
    > $header =~ s/\cM//g ; # MS CR/LF
    > $header =~ s/zone//ig ;
    > $header =~ s/\s+//g ;


    > my @zones = split /,/ , $header ;
    > shift @zones ; #first one is empty
    > foreach ( @lines ) {
    > chomp ;
    > s/\cM//g ;
    > my @prices = split /,/ ;
    > my $kg = shift @prices ;


    > foreach ( @zones ) {
    > $priceHash{$kg}{$_} = shift @prices ;


    Your problem is that you use floating point numbers as hash keys
    and rely on some automatic conversion between strings and floating
    point values.

    In this line

    $priceHash{$kg}{$_} = shift @prices ;

    '$kg' will be a floating point number you just read from the file.
    This could be e.g. "5.0" within the file but since floating point
    numbers only have a limited precision the key in the hash could
    actually be 4.9999999999 or 5.000000000001 or something similar.
    So you end up with an element that has a key of e.g. 4.9999999999
    and that, of course, won't be found when you look for an element
    by the number 5 as the key. One way around that could be to use
    double quotes arount the '$kg' to avoid conversion to a floating
    point number, so the key isn't whatever approximation you have
    for 5.0 but the string "5.0". Of course, to retrieve values from
    the hash you will also need strings, not floating point numbers
    and they must fit exactly what you used when you created the
    hash, so you must use "5.0" and not "5" as the key.

    But I would strongly recommend that you avoid anything related
    to floating point numbers as keys. It's going to be extremely
    hard to get right and there must be better ways to create keys
    for your hash.
    Regards, Jens
    --
    \ Jens Thoms Toerring ___
    \__________________________ http://toerring.de
     
    Jens Thoms Toerring, May 1, 2007
    #4
  5. Justin C

    Justin C Guest

    In article <CNKZh.7221$>, Mumia W. wrote:
    > On 05/01/2007 10:39 AM, Justin C wrote:
    >> I can't seem to access the values in a hash of hashes, but if I iterate
    >> over it I'm able to print all the values. The error I get if I try to
    >> access one value is :
    >> Use of uninitialized value in print at ./test line 20, <PRICES> line 62.
    >> [...]

    >
    > Your program is so close to working, but you have one problem--you
    > forgot that hash keys are strings and only strings. Where string
    > comparisons are concerned, '5' != '5.0'. Change $weight (at the top of
    > the program) to "5.0" and rerun your program.


    Hash keys are strings! Of course! Thank you for pointing this out, I'd
    been staring at it for hours and just could not see it.


    > However, it's better to create the hash keys as simpler numbers, e.g.
    > insert 5 rather than 5.0:
    >
    > $priceHash{$kg+0}{$_} = shift @prices;


    I don't understand what's going on here. $kg is a string, the string is
    "5.0", yet "$kg+0" removes ".0". Please tell me what's going on, it's
    confusing and my brain is already a little bruised from today's work!


    > This causes a warning about "per" which has some function in your data
    > that I don't know about.


    Ah, yes, I need to skip that line too, I'll fix my script to ignore it.


    > I also have two minor nickpicks: "open" is misspelled in the die()
    > statement,


    That *is* a nit pick!


    > and the PRICES file is never explicitly closed by program.


    I'm sure I read here that file-handles are closed automatically when
    they go out of scope, I've not bothered closing a file since. Is this
    bad?


    > You also do a little more work than is necessary to read in the data.


    I know, I don't do anywhere near enough perl to be proficient, though I
    am trying!


    > This is a shorter version of your program:

    [snip]

    Lots of better ways of doing things. Thank you for pointing them out.

    Justin.

    --
    Justin C, by the sea.
     
    Justin C, May 1, 2007
    #5
  6. Jens Thoms Toerring wrote:
    >
    > Your problem is that you use floating point numbers as hash keys
    > and rely on some automatic conversion between strings and floating
    > point values.
    >
    > In this line
    >
    > $priceHash{$kg}{$_} = shift @prices ;
    >
    > '$kg' will be a floating point number you just read from the file.
    > This could be e.g. "5.0" within the file but since floating point
    > numbers only have a limited precision the key in the hash could
    > actually be 4.9999999999 or 5.000000000001 or something similar.
    > So you end up with an element that has a key of e.g. 4.9999999999
    > and that, of course, won't be found when you look for an element
    > by the number 5 as the key.


    No, that is not correct. The data is read from the file and stored in Perl's
    scalars as strings and is not converted to a floating point number because it
    is not used in any mathematical operations.


    John
    --
    Perl isn't a toolbox, but a small machine shop where you can special-order
    certain sorts of tools at low cost and in short order. -- Larry Wall
     
    John W. Krahn, May 1, 2007
    #6
  7. Mumia W. wrote:
    > On 05/01/2007 10:39 AM, Justin C wrote:
    >> I can't seem to access the values in a hash of hashes, but if I iterate
    >> over it I'm able to print all the values. The error I get if I try to
    >> access one value is :
    >> Use of uninitialized value in print at ./test line 20, <PRICES> line 62.
    >> [...]

    >
    > Your program is so close to working, but you have one problem--you
    > forgot that hash keys are strings and only strings. Where string
    > comparisons are concerned, '5' != '5.0'. Change $weight (at the top of
    > the program) to "5.0" and rerun your program.
    >
    > However, it's better to create the hash keys as simpler numbers, e.g.
    > insert 5 rather than 5.0:
    >
    > $priceHash{$kg+0}{$_} = shift @prices;
    >
    > This causes a warning about "per" which has some function in your data
    > that I don't know about.
    >
    > I also have two minor nickpicks: "open" is misspelled in the die()
    > statement, and the PRICES file is never explicitly closed by program.
    > You also do a little more work than is necessary to read in the data.
    >
    > This is a shorter version of your program:
    >
    > use strict;
    > use warnings;
    > use File::Slurp;
    >
    > my %priceHash;
    > my $weight = 5;
    > my $zone = 5;
    >
    > my @lines = read_file('pforcePrices.csv');
    > s/\cM\cJ$// for @lines;
    >
    > my $header = shift @lines;
    > $header =~ s/(zone|\s+|^,)//ig;
    > my @zones = split /,/,$header;
    >
    > foreach my $line (@lines) {
    > next unless $line =~ /^\d/;
    > my @prices = split /,/,$line;
    > my $kg = shift @prices;
    > foreach my $zn (@zones) {
    > $priceHash{$kg+0}{$zn} = shift @prices;
    > }
    > }
    >
    > print "Price: $priceHash{$weight}{$zone}\n";


    I'd do it more like this instead: :)

    open my $PRICES, '<', 'pforcePrices.csv'
    or die "Cannot open 'pforcePrices.csv' $!";

    ( my $header = <$PRICES> ) =~ s/\s+\z//;
    my ( undef, @zones ) = map { s/\s*zone\s*//i; $_ } split /,/ , $header;

    my %priceHash;
    while ( <$PRICES> ) {
    s/\s+\z//;
    my ( $kg, @prices ) = split /,/;
    @{ $priceHash{ $kg } }{ @zones } = @prices;
    }




    John
    --
    Perl isn't a toolbox, but a small machine shop where you can special-order
    certain sorts of tools at low cost and in short order. -- Larry Wall
     
    John W. Krahn, May 1, 2007
    #7
  8. On Tue, 01 May 2007 19:49:46 -0000,
    Justin C <> wrote:
    > In article <CNKZh.7221$>, Mumia W. wrote:
    >> On 05/01/2007 10:39 AM, Justin C wrote:


    >> However, it's better to create the hash keys as simpler numbers, e.g.
    >> insert 5 rather than 5.0:
    >>
    >> $priceHash{$kg+0}{$_} = shift @prices;

    >
    > I don't understand what's going on here. $kg is a string, the string is
    > "5.0", yet "$kg+0" removes ".0". Please tell me what's going on, it's
    > confusing and my brain is already a little bruised from today's work!


    $kg + 0 is an arithmetic expression, which perl calculates. The result
    of it is a number (not a string). That number happens to be 5. The
    number then gets stringified, because it is used as a hash key, and the
    result is "5". There is a lot of stuff going on behind the scenes, which
    has to do with the way Perl's scalars can represent all these different
    things like strings, integers, doubles, references, etc.

    Watch:

    $ perl -w
    my $a1 = 5.0;
    my $a2 = "5.0";
    print "$a1 $a2\n";
    $a2 += 0;
    print "$a1 $a2\n";
    __END__
    5 5.0
    5 5

    Generally, if you want numbers to be stringified in a particular format
    you should use (s)printf.

    Martien
    --
    |
    Martien Verbruggen | We are born naked, wet and hungry. Then
    | things get worse.
    |
     
    Martien verbruggen, May 1, 2007
    #8
  9. Justin C <> wrote:
    > In article <CNKZh.7221$>, Mumia W. wrote:


    >> and the PRICES file is never explicitly closed by program.

    >
    > I'm sure I read here that file-handles are closed automatically when
    > they go out of scope,



    That is correct.


    > I've not bothered closing a file since.



    perl will always do what it is supposed to do, it is a machine...


    > Is this
    > bad?



    .... but that does not lead to the conclusion that the human telling
    perl what to do will do what _it_ is supposed to. :-(

    See my tale of woe from leaving out a close():

    http://groups.google.com/group/comp.lang.perl.misc/msg/73d4587743c64e2f


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, May 2, 2007
    #9
  10. Justin C

    Justin C Guest

    On 2007-05-01, Tad McClellan <> wrote:
    > Justin C <> wrote:
    >> In article <CNKZh.7221$>, Mumia W. wrote:

    >
    >>> and the PRICES file is never explicitly closed by program.

    >>
    >> I'm sure I read here that file-handles are closed automatically when
    >> they go out of scope,

    >
    >
    > That is correct.
    >
    >
    >> I've not bothered closing a file since.

    >
    >
    > perl will always do what it is supposed to do, it is a machine...
    >
    >
    >> Is this
    >> bad?

    >
    >
    > ... but that does not lead to the conclusion that the human telling
    > perl what to do will do what _it_ is supposed to. :-(
    >
    > See my tale of woe from leaving out a close():
    >
    > http://groups.google.com/group/comp.lang.perl.misc/msg/73d4587743c64e2f


    OK, point taken. Explicit close() added... now to grep the rest of what
    I'd written and see how many have more open()s than close()s :)

    Justin.

    --
    Justin C, by the sea.
     
    Justin C, May 2, 2007
    #10
    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. rp
    Replies:
    1
    Views:
    596
    red floyd
    Nov 10, 2011
  2. Scott  Gilpin
    Replies:
    2
    Views:
    238
  3. Perl Learner

    Hashes of hashes or just one hash ?

    Perl Learner, Jun 8, 2005, in forum: Perl Misc
    Replies:
    11
    Views:
    235
  4. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    237
  5. Replies:
    3
    Views:
    233
Loading...

Share This Page