Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

decimal numbers as hash keys

by Alex31 (Initiate)
on Jun 26, 2013 at 15:17 UTC ( #1040802=perlquestion: print w/replies, xml ) Need Help??
Alex31 has asked for the wisdom of the Perl Monks concerning the following question:

I have decimal numbers as hash keys. These numbers are coming from a text stored in $title and parsed by a REGEX. It works, unless there is a 0 on last decimal position.

The file test.txt contains a title (one per line) (see table below - $title)

%ifac= ( 14.152 => "JCI", 9.334 => "JHEP", 5.745 => "JIMM", 6.270 => "JID", 1.480 => "JRN", 6.105 => "KI" ); open (FH, "<test.txt"); my $zler=0; while(defined(my $title=<FH>)) { if ($title=~m|.{10,}\(IF ([\.\d]+)\)|) { $id=$zler; $factor=$1; $jour=$ifac{$factor}; print "INSERT INTO table (id, key, value) VALUES ($id,'ifac','$jou +r');\n"; } $zler++; } close(FH);

Result of $title, $factor, $jour would be for example:

(as it is)
1. This is a test. (IF 6.270)6.270JIDundef
2. Another test, working and resulting in JHEP. (IF 9.334)9.334JHEPJHEP
3. Last test, which does not work. (IF 1.480)1.480JRNundef

If $factor results in 6.270 or 1.480 then $jour is undefined but should result in JID or JRN.

I tried a direct access to the hash with

$jour= $ifac{6.270} $jour= $ifac{6.27} $jour= $ifac{"6.270"}

In these cases $jour results in JID (except for the "6.270" undefined (this is OK, as the hash key is a number and not a string)

What is the difference between the direct access and access of hash key via the variable $ifac{$factor}? It seems that the last 0 is the problem. But in direct access it would work, even if I leave out the last 0.

Regards, Alex

Replies are listed 'Best First'.
Re: decimal numbers as hash keys
by kennethk (Abbot) on Jun 26, 2013 at 15:27 UTC
    If you need a particular format of your number in order to get a desired behavior, you need to control how your number gets converted into a string. This is usually done using sprintf. For example
    $ifac{sprintf "%.3f" $factor}
    will cause the key accessed to be a floating point number with 3 decimal places, thus forcing the numbers to yield appropriate strings. It also avoids the potential 6.269999999999999999 problem.

    In your case, your issue begins with your hash creation:

    use Data::Dumper; my %ifac= ( 14.152 => "JCI", 9.334 => "JHEP", 5.745 => "JIMM", 6.270 => "JID", 1.480 => "JRN", 6.105 => "KI" ); print Dumper \%ifac;
    $VAR1 = { '6.105' => 'KI', '1.48' => 'JRN', '9.334' => 'JHEP', '5.745' => 'JIMM', '6.27' => 'JID', '14.152' => 'JCI' };
    You need to control string format on input, which means using strings not numbers for initialization:
    my %ifac= ( '14.152' => "JCI", '9.334' => "JHEP", '5.745' => "JIMM", '6.270' => "JID", '1.480' => "JRN", '6.105' => "KI" );

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Thank you for your fast reply.

      So even if it would work with a direct access, it is always better to translate the hash keys to a string.

      Just an idea: Another solution would be to cut the last 0 from $factor - which should then also work?

        The hash key is a string. If you don't do explicit stringification, Perl does it for you. In your case, it does it inconsistently between your construction and access.

        You would get identical functionality by trimming trailing zeroes (s/0+$//), but I think that behavior is less obvious, in which case you are setting yourself up for painful maintenance. Another option would be to multiply your factors by 1000, and thus deal with integers instead of decimals.

        Update: An improvement on the "trim trailing zeroes" front would be to use the same mechanism that mangled the input in the first place. If you swap to

        you'll invoke the internal Perl conversion to a number and reconvert back to a string for hash access, and hence should hit the correct record.

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: decimal numbers as hash keys
by arnaud99 (Beadle) on Jun 26, 2013 at 15:51 UTC


    To add to the post above, a simple use of Data::Dumper highlights the issue

    use strict; use warnings; use autodie; #these 3 above are a must -have use Data::Dumper; my %ifac= ( 14.152 => "JCI", 9.334 => "JHEP", 5.745 => "JIMM", 6.270 => "JID", 1.480 => "JRN", 6.105 => "KI" ); print Dumper(\%ifac);


    $VAR1 = { '6.105' => 'KI', '1.48' => 'JRN', '9.334' => 'JHEP', '5.745' => 'JIMM', '6.27' => 'JID', '14.152' => 'JCI' };



Re: decimal numbers as hash keys
by sundialsvc4 (Abbot) on Jun 26, 2013 at 16:28 UTC

    It is my understanding, and I think that the last post confirms it, that the key for any hash is always “a string.”   Numbers get converted by some means into “a string,” and particularly in the case of floats, that can get dicey.   I frankly wouldn’t try to use a float, for fear of failing to locate a value that really is supposed to be “in” the hash.

    What to do .. how much complexity is actually appropriate to invest in this .. depends on your situation, but one strategy that you might wish to consider is to use int() to extract the integer portion of the number (or perhaps, the integer portion of some agreed-upon multiple of it), then have the hash-bucket actually consist of a list of, say, hashes that contain (key, value) pairs.   (Which can be fairly-efficiently searched through using grep().)   So, a dump of what I’m talking about here might look like this:

    [ '1' => [ { 'value' : 1.61803398874, 'name' : 'golden_ratio' }, { 'value' : 1.30357, 'name' : 'conways_constant' }, '2' => [ { 'value' : 2.71828, 'name' : 'E' }, ], '3' => [ { 'value' : 3.14159, 'name' : 'pi' }, ] ]

    And if we had hashed by int($key * 10), we would have had four distinct buckets with keys (13, 16, 27, 31) with only one entry apiece.   So it goes.)

    You would, of course, embed all of this messy-ness into a class ... but, now you have a data structure that can store floating-point values exactly, yet find them reasonably quickly by using a hash to reduce the search time.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1040802]
Front-paged by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (10)
As of 2018-11-15 10:38 GMT
Find Nodes?
    Voting Booth?
    My code is most likely broken because:

    Results (182 votes). Check out past polls.

    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!