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

Hi Monks, I need to delete a hash element which has O (or "0" or "0.0") as its value. ( the key can be anything)
I tried the below code and delete doesn't return 1 when the value is 0
Thank you for your help,
Sush

my %hash ;
my $key ="SOME STUFF";
my $value = "0";

$hash{$key}=$value;
print "$key => $hash{$key} \n ";
delete ($hash{$key}) || print " There is some error : $! ";

-- Read the replies
Got the problem.

Thanks for the help, Monks.

Replies are listed 'Best First'.
Re: Delete a zero-valued hash element
by FunkyMonk (Bishop) on Oct 02, 2007 at 19:43 UTC
    delete returns a list of the values deleted. Your delete will return 0 causing your error message to be printed.

Re: Delete a zero-valued hash element
by runrig (Abbot) on Oct 02, 2007 at 19:43 UTC
    $! will not have any useful content. $! is only good for system functions. Also, delete returns the value of the element deleted, and since the string "0.0" is true, you will get your error message. If you know all of your values are numeric then just use '== 0'. E.g.
    for ( keys %hash ) { delete $hash{$_} if $hash{$_} == 0; }
Re: Delete a zero-valued hash element
by blue_cowdawg (Monsignor) on Oct 02, 2007 at 19:49 UTC
        I tried the below code and delete doesn't return 1 when the value is 0

    and nor would I expect it to.

    Given the following code:

    my %hash=( foo=> 1, bar => 2, fazz=> 0 ); my $rc = delete $hash{bar}; printf "%s\n",$rc; $rc = delete $hash{fazz}; printf "%s\n",$rc;
    I get the following results:
    2 0

    If you read the perldoc for the delete function you'll notice:

                   Returns a list with the same number of elements as the number
                   of elements for which deletion was attempted.  Each element of
                   that list consists of either the value of the element deleted,
                   or the undefined value.  In scalar context, this means that you
                   get the value of the last element deleted (or the undefined
                   value if that element did not exist).
    
    
    So.. if your value for the key is zero anyway then you'll just return a zero in the scalar context.

    Clear as mud?


    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
Re: Delete a zero-valued hash element
by kyle (Abbot) on Oct 02, 2007 at 19:55 UTC

    Have a look at the delete documentation. One big thing to notice is that it does not set $! if there's an error, so there's no point in reporting it.

    If you really want to test how many things were deleted, you can assign to an array like this:

    my @deleted = delete $hash{$key}; (1 == scalar @deleted) || print "oops";

    ...or you can do some reference judo:

    (1 == scalar @{[delete $hash{$key}]}) || print "oops";

    Note however, this doesn't actually tell you how many were deleted but rather how many you asked to be deleted. Each item that you tried to delete that was already gone will be undef in the resulting list. You could say something like this:

    scalar grep { defined } delete $hash{$key};

    ...but this will be wrong in the case where one of the things deleted actually was the undef value.

    Basically, I don't think you can check delete for errors.

      ...or you can do some reference judo:

      You can clean up a lot of the ugly with:

      print "oops" unless () = delete $hash{$key};

      Here's proof:

      use YAML; use Test::More tests => 3; my %hash = map { $_ => $_ } 0 .. 3; for my $key (0 .. 1) { pass( "deleted $key" ) if () = delete $hash{$key}; } is( keys %hash, 2, "deleted two keys" ) or warn Dump \%hash;
Re: Delete a zero-valued hash element
by shmem (Chancellor) on Oct 02, 2007 at 19:57 UTC
    Use
    length delete ($hash{$key}) || print " There is some error : $! ";

    update - Oops. A kyle states below, length doesn't work with zero length hash values. So, use

    defined delete ($hash{$key}) || print " There is some error : $! ";

    Ah.. well... I can see the next problem: what if you say $hash{$key} = undef? In that case defined(delete) will fail

    perl -le '$h{""}=undef; print ">",defined delete $h{""},"<"' ><

    since delete returns undef here. So, probably best advice is:

    • don't stuff values into a hash using a null string or undef as key
    • don't stuff empty values / undef as a value into a hash
    • if you delete those emptinesses, don't care too much about the return value.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      That will tell you the length of the value deleted, not how many items were deleted.

      my %h = ( foo => 'foo123', bar => 'bar456', nak => '' ); print length delete $h{foo}, "\n"; print length delete $h{nak}, "\n"; print scalar @{[delete $h{bar}]}, "\n"; __END__ 6 0 1
Re: Delete a zero-valued hash element
by perlfan (Parson) on Oct 02, 2007 at 19:55 UTC
    If your hash index is based on value and you utilize anonymous arrays, then you can make your life a lot easier.

    For example:
    %hash = (x => 0, y => 0, z => "O", a => "O", b => 5, c => 5, d => 6, e + => 0.0);
    Could look like:
    %hash = (0 => [x,y], O => [z,a], 5 => [b,c], 6 => [d], '0.0' => [e]);
    And you'd only have to delete($hash{O});, etc...

    If this change in data structure screws your code up, then try to refactor or keep a similarly structured hash as a "look up" for the elements in the actual hash you'd like to delete.
A reply falls below the community's threshold of quality. You may see it by logging in.