Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I am trying to compare the value from a hash table with below code but getting the error message "Can't use string ("2") as a HASH ref while "strict refs" in use at line 209 ". I do not understand what's going wrong, if i remove  use strict code works perfectly, but I know thats not the solution. Please help me out with your suggestions.
foreach $item (keys %Myhash){ if ($item eq $My_Value) { foreach $iteminitem (sort(keys %{$Myhash{$item}})){ foreach $iteminitem1 (sort(keys %{$Myhash{$item}{$iteminitem}} +)){ if ($iteminitem1 eq 'name' && $user_number == $Myhash{$item}{ +$iteminitem}{$iteminitem1}) { my $User = $iteminitem; } } } } }

Replies are listed 'Best First'.
Re: Can't use string ("2") as a HASH ref while "strict refs" in use
by TGI (Parson) on Jul 17, 2008 at 17:49 UTC

    I don't want to sound harsh, but your code makes my eyes bleed. The good news is that there are a few easy steps you can take to make it much easier to read, understand, and maintain.

    Use descriptive variable names. Names like %Myhash are fine in CS textbook examples, but in real code they are pure evil. What kind of information does the hash contain? Put it in the name. Something like %system_info can be much easier to understand.

    Use consistent capitalization and formatting in your variable names. Find a style you like and stick to it. Generally variable names are lower_case_with_underscores but some people prefer initialLowerCaseWithCamelCaseAfter. Take a look at perlstyle.

    Banishing $iteminiteminiteminitem and friends will take care of 90% of the problem with this code. What else can we do to fix it?

    Format your code for readability. Break up long lines. Use whitespace. Use consistent indentation.

    foreach $item (keys %Myhash){ if ($item eq $My_Value) { foreach $iteminitem (sort(keys %{$Myhash{$item}})){ foreach $iteminitem1 (sort(keys %{$Myhash{$item}{$iteminit +em}})){ if( $iteminitem1 eq 'name' && $user_number == $Myhash{$item}{$iteminitem}{$itemi +nitem1} ) { my $User = $iteminitem; } } } } }

    All I did was reformat your code and it is a fair bit easier to read. You might want to take a look at perltidy. It does a pretty good job of reformatting perl code for you. Some people require that all code be passed through perltidy before check in to their source repositories.

    Remember that you should be writing your code so that you will understand it 6 months from now, when you get a call at 4am on a Saturday morning about the emergency bug that must be fixed right away.

    Well written code is primarily a document written to communicate the functioning of the system for maintenance programmers. The fact that it runs and does work is just a side effect of a well documented system.

    OK, the last statement isn't really true, but I've found that keeping this point of view in mind helps to improve the readability of my code.

    After cleaning up your code a bit, I noticed that you seem to be doing some unnecessary loops.

    # This: foreach my $top_item ( keys %Myhash ){ if ($top_item eq $My_Value) { # do stuff } } # can be reduced to this: if ( exists $Myhash{$My_Value} ) { # do stuff } # And this: foreach $iteminitem1 (sort(keys %{$Myhash{$item}{$iteminitem}})){ if( $iteminitem1 eq 'name' && $user_number == $Myhash{$item}{$iteminitem}{$itemi +nitem1} ) { my $User = $iteminitem; } } # can be reduced to this: if ( exists $Myhash{$My_Value}{$iteminitem}{'name'} and $user_number == $Myhash{$My_Value}{$iteminitem}{'name'} ) { my $User = $iteminitem; } # The combined restructuring yields: if ( exists $Myhash{$My_Value} ) { foreach $iteminitem (sort(keys %{$Myhash{$item}})){ if ( exists $Myhash{$My_Value}{$iteminitem}{'name'} and $user_number == $Myhash{$My_Value}{$iteminitem}{'name'} ) { my $User = $iteminitem; } } }

    That eliminates several unneeded sorts and a fair bit of looping over arrays.

    Keep working on your code, you'll get better. Improving the readability of your code will make it easier to debug as you do your initial development and easier to add features and debug in the future. Learning a few simple habits now will increase your productivity, make your teeth whiter and brighter, and get you the girls.


    TGI says moo

      Hi Monks, Thanks for the response. I am sorry to say that i am new to programming. I'll definitely look into the coding style that you suggested. The url's that you have provided is of great help. Once again thanks for the suggestions.
Re: Can't use string ("2") as a HASH ref while "strict refs" in use
by pileofrogs (Priest) on Jul 17, 2008 at 17:00 UTC

    Basically, $Myhash{$item} == 2, not a ref to a hash like you think it does. Your problem is probably in where you assign the values, not in the code you showed us.

    Also, why are you iterating over hashes to find a value? You could just use the hashes.

    $item = $Myhash{$My_Value};

    and so on...?

    Try adding this:

    use Data::Dumper; ... your code ... print Dumper(\%Myhash)."\n";

    And see if %Myhash looks like you think it does.

      Hi thanks for the response. please find the hash table that i defined. using the Data::Dumper the structure is desplayed as expected.
      %Myhash = ( 'SuperUser' => { 'Username' => '1', 'UserPage 0' => { 'UserPage_num' => '0', 'data' => { 'field1' => { 'name' => 'Version', 'Size' => '1' }, 'field2' => { 'name' => 'Length', 'Size' => '6' }, 'field3' => { 'name' => 'Number', 'Size' => '8' }, }, }, 'UserPage 1' => { 'UserPage_num' => '2', 'data' => { 'field1' => { 'name' => 'Version', 'Size' => '4' }, 'field2' => { 'name' => 'Length', 'Size' => '8' }, 'field3' => { 'name' => 'Number', 'Size' => '8' }, + }, }, And so on....

      Here all the values are predefined so i am not making any changes in the Hash table. I am just trying to retrieve the values from the hash table. Here is the code where i am getting issue with in line no 209
      206 foreach $item (keys %Myhash){ 207 if ($item eq $My_Value) { 208 foreach $iteminitem (sort(keys %{$Myhash{$item}})){ 209 foreach $iteminitem1 (sort(keys %{$Myhash{$item}{$iteminitem}} +)){ 210 if ($iteminitem1 eq 'name' && $user_number == $Myhash{$item}{ +$iteminitem}{$iteminitem1}) { 211 my $User = $iteminitem; 212 } 213 } 214 } 215 } 216 }
        as CountZero says -- you've got a situation where the you're referencing something that contains only a value and not a hash ref:

        Using your Data structure we walk trough your code and substitute in the values to see the problem.

        $My_Value = 'SuperUser'; # assumption for this exercise foreach $item (keys %Myhash){ # $item = 'SuperUser' first pass through if ($item eq $My_Value) { foreach $iteminitem (sort(keys %{$Myhash{'SuperUser'}})){ # $iteminitem = 'Username' first pass through foreach $iteminitem1 (sort(keys %{$Myhash{'SuperUser'}{'Us +ername'}})){ ## Here's the problem: $Myhash{'SuperUser'}{'Username'} = 2 if ($iteminitem1 eq 'name' && $user_number == $Myhash{$item}{ +$iteminitem}{$iteminitem1}) { my $User = $iteminitem; } } } } }

        What you probably want to do is check that you have a hash ref in $iteminitem

        wrap the line 209 loop in a check

        if (ref $iteminitem eq 'HASH') { foreach $iteminitem1 (sort(keys %{$Myhash{$item}{$iteminitem}})){ .... } }
        Or perhaps it's time to refactor the code a bit, but without further context that's difficult to say

        I have a hunch that one of the hashes is being interpreted as a list and then a scalar. That would produce what you're seeing. E.G. try this:

        print scalar(%hash = (name => "bob"));

        You get the number 2 because there's only two items in the list form of the hash (name and "bob").

        --Pileofrogs

        A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Can't use string ("2") as a HASH ref while "strict refs" in use
by jettero (Monsignor) on Jul 17, 2008 at 16:46 UTC
    This example doesn't have a line 209, but one of the places you use %{ } or {key}{key}{key} is trying to deref a "2". It seems to me that in the code that builds the hash structure, you have a $myhash{key} = 2;

    Without strict refs, something odd happens:

    use Data::Dumper; no strict 'refs'; $x = 2; $x->{hrm} = "weird"; print "", Dumper([%{2}]), "\n"; # odd

    It's allowing you to build a hashref with the name of "2", but it almost certainly isn't what you want.

    -Paul

Re: Can't use string ("2") as a HASH ref while "strict refs" in use
by kyle (Abbot) on Jul 17, 2008 at 16:58 UTC
Re: Can't use string ("2") as a HASH ref while "strict refs" in use
by CountZero (Bishop) on Jul 17, 2008 at 17:31 UTC
    Are you sure $Myhash{$item} or $Myhash{$item}{$iteminitem} contain hash references? From the error message I think (nay, I'm quite certain) that they contain simple hash values.

    Go to the code which assigns some data into this hash and see what is being put into it.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Can't use string ("2") as a HASH ref while "strict refs" in use
by ikegami (Patriarch) on Jul 18, 2008 at 04:36 UTC

    The most common cause for having a number where a reference is expected is when

    $Myhash{$item} = @array;

    is used instead of

    $Myhash{$item} = \@array;