Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

One liner to Check and undef hash elements

by Anonymous Monk
on Apr 15, 2003 at 23:07 UTC ( [id://250725]=perlquestion: print w/replies, xml ) Need Help??

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

Hi monks, I have a hash of elements where each element can be either an undef or a value:
$hash->{'a'} = undef; $hash->{'b'} = 4.5; $hash->{'c'} = 6; $hash->{'d'} = undef; ...
Is there a quick, perlish way to perform either of these 2 operations:
1) check if the hash is empty (all elements are undef) 2) reset all elements to undef
I know the obvious ways to do these, I'm kinda looking for a creative one-liner for each operation. thanks, Michael
edited by boo_radley for title : was "hash problem"

Replies are listed 'Best First'.
•Re: One liner to Check and undef hash elements
by merlyn (Sage) on Apr 16, 2003 at 02:13 UTC

      Nice! I knew there had to be a better way. Regarding problem 1, what exactly does ->() do? I know that without it, a coderef would be returned, and with it, the result is returned. But how does it work? Does it evaluate the sub on the spot or in the moment I access $hash_has_non_undef?

        sub { ... } as an expression returns a reference to an anonymous function. ->(@list); takes a reference to an anonymous function and calls it. So what happens when you chain them is you define an anonymous function and call it on the spot, the anonymous function being lost, because you didn't save the reference anywhere.

        Makeshifts last the longest.

      Doh, I knew there was a single step approach for Nr.2.. it just wouldn't occur to me last night.

      I'm not sure I like your approach to the first one though - using a subref to get around the lack of control in naked blocks seems to hackish. I'm not really happy with my own alternative, either:

      my $hash_has_non_undef = do { local $_; my $found = 0; defined && ($found++, last) while (undef, $_) = each %hash; $found; };
      But without Perl 6 junctions there's not much that can be done about it if you need shortcircuiting..

      Makeshifts last the longest.

        Well, the non-sub way could look like this:
        my $hash_has_non_undef = do { my $found; until($found or not (undef, m +y $v) = each %hash) { $found = defined $v } $found };
        I mean, that's the way we had to do it in Pascal. Stupid boolean flag variables all over the place because they wouldn't let us exit blocks. {grin}

        Doh! As I'm staring at this, I realize the $v can do double duty.

        my $hash_has_non_undef = do { my $v; until($v or not (undef, $v) = eac +h %hash) { $v = defined $v } $v };
        But that's optimized for Golf, not for maintenance. Ick.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

Re: One liner to Check and undef hash elements
by Aristotle (Chancellor) on Apr 16, 2003 at 02:04 UTC
    # 1.) scalar grep defined, values %$hash; # 2.) undef $_ for values %$hash;
    At 2.), I interpreted your question as in just emptying all slots, without removing their keys.

    Makeshifts last the longest.

Re: hash problem
by crenz (Priest) on Apr 15, 2003 at 23:15 UTC

    I'm sure somebody else will come up with something more optimal...

    #1. if ((scalar grep {defined} values %{$hash}) == 0) { # all values are undef } #2. $hash = { map {$_ => undef} keys %{$hash} }; # now empty

    Update: Changed > 0 to == 0, since the poster asked for "check if empty"

    Update: Fixed precedence problem (pointed out by original poster). Works now.

      Hi crenz, thanks for you reply. #2 works very nicely, but I can't make #1 work. Your code prints "empty" for both of these hashes.
      $hash->{'a'} = 1; $hash->{'b'} = undef; $hash->{'c'} = undef; $hash->{'d'} = undef; if (scalar grep {defined} values %{$hash} == 0) { print "empty\n"; } $hash->{'a'} = undef; $hash->{'b'} = undef; $hash->{'c'} = undef; $hash->{'d'} = undef; if (scalar grep {defined} values %{$hash} == 0) { print "empty\n"; }
Re: Checking and undef'in hash elements in one step?
by grantm (Parson) on Apr 16, 2003 at 01:10 UTC
    Is there a quick, perlish way to perform either of these 2 operations

    What about a way to do both :-)

    if(scalar @{[ delete @hash{keys %hash} ]}) { print "Hash was not empty\n"; } else { print "Hash was empty\n"; }

    The delete function returns a list of the deleted values. If there were no keys in the hash then the list will be empty. I found that if the hash contained one key with a value of undef then scalar delete @hash{keys %hash} evaluated to false - hence the anonmous array gymnastics.

    My code is actually checking whether the hash was empty (and in the same step emptying it), but to go back to your original question, you define empty as "all elements are undef" which is a mighty unusual definition of empty. Surely if it's empty, there are no elements.

    You may be confused by the fact that refering to a hash key that does not exist returns undef. If the hash key does exist, but its value is undef, then the hash is not empty - it has at least one key. The 'exists' function is what you'd use to check for the existence of a particular key.

    If your hash might have undefined values but you just want to know if there are any defined values, you could say:

    if(grep defined($_), values %hash) { ... }
      What about a way to do both :-)

      TMTOWTDI (couldn't figure out how to do this with just an "or" or an "and")

      use strict; use warnings; use Data::Dumper; my $hash; $hash->{'a'} = 1; $hash->{'b'} = undef; $hash->{'c'} = undef; $hash->{'d'} = undef; unless (grep {defined $$hash{$_} xor $$hash{$_}=undef} keys %{$hash} ) + { print "empty\n";} else { print "Not empty\n"; } print Dumper \$hash;

      -enlil

Re: Checking and undef'in hash elements in one step?
by l2kashe (Deacon) on Apr 16, 2003 at 00:22 UTC
    Update: The first line is why this node is garnishing negative XP. Its wrong for a few reasons..both reasons are within the unless...

    First off unless will only evaluate to true if the EXPR inside returns something other than '0', an empty sting '', or undef

    Second is the NULL. In this context I was attempting to use it as a alias for '0' which is what perl does, but that was wrong because if we do 'unless( values(%hash) );' that works as well. If we had applied 'use strict;' to this code it would have broke as NULL is a bareword in this situation.

    Justifications:
    First: I've been coding a bunch of C lately as should be readily apparent to whomever knows C in the bunch. I just didn't fully context switch to perl when whipping out this code.
    Second: There was a thread ( I will attempt to find and link to) which raised an interesting situation where when calling values on a newly created hash ( cant remember if it was actually a ref to a hash or not), the values would be autovivified. In that situation I ended up suggesting doing 'unless( join('', values %hash) );' which was the only way to consistantly check to see if the hash really had anything in it.

    Sorry for the bad code, but I guess we can stop downvoting this node, and simply use it as an example for .... something somewhere I'm sure..
    # will print "no keys" if all values are undef.. print "no keys\n" unless( values(%hash) != NULL ); # to resest a hash.. %hash = (); # or if you are paranoid undef(%hash) && %hash = ();
    Happy hacking

    MMMMM... Chocolaty Perl Goodness.....
        Perhaps saying that NULL isn't a keyword would be more constructive, or that he meant undef instead.
        cb reference!:
        now, what are the odds I log in in the morning to find all my posts mysteriously downvoted?
        UPDATE:Awww. not downvoted at all.

        feanor_269
        It was more to display the idea .. but its syntax is correct and in testing provided acurate results.. I've now garnished quite a few negative XP for this nodelet.. Thanks for at least responding to the post...

        Now why dont people like this??

        should it have said "no values were defined?" which matches what was asked for?.. should I have left it as unless( values(%hash)) ???

        after testing some more I now see that the values(%hash) is sufficient.. I stand corrected and accept the negative XP..
        /me dohs and hangs his head in shame...

        MMMMM... Chocolaty Perl Goodness.....
        A Perl line with syntax error is still Perl.

        To be frank, I don't see the point of your last line, that Q&A. It should not be too difficult for people to be nice to each other. Make pointless comment (I point to your last line) is an insult to the merit of the poster, and the community at large. I don't see this funny at all.

        I read your disclaimer, but it does not entitle you to be rude, or gives you the excuse to be rude. Doesn't matter whether it is for a good purpose or a bad one, the way you expressed yourself is rude, and there is no need to put things in that way.

        I point out this to you for your benefit, to be sincere. I don't jump on you because of this single incident, but you have been doing this to people from time to time.

        Well, I understand how you behave yourself is really your decision.

        As you said the syntax is wrong, let me tell you now, from a pure syntax point of view, this is perfectly correct Perl syntax.

        Didn't you realize that NULL could be a constant?
        use strict; use constant NULL => ""; my @a = (1,2,3); my @b = (); my @c = (""); print "a is empty: " . is_null(@a), "\n"; print "b is empty: " . is_null(@b), "\n"; print "c is empty: " . is_null(@c); sub is_null { my @a = @_; (@a == NULL) ? "true" : "false"; }
Re: One liner to Check and undef hash elements
by Anonymous Monk on Apr 16, 2003 at 13:29 UTC
    Wow! You guys never cease to amaze me. thanks to everyone. Michael

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://250725]
Approved by Paladin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (8)
As of 2024-03-29 08:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found