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

I've got a list of hashkeys. I need to figure it if all of them are defined or not. Instead of doing something as ugly as:

if (defined $hash->{A} && defined $hash->{B} && defined $hash->{C}) { }
I would much rather do something more Perl-ish. I came up with the following:

my $hash = { A => 1, B => 1, C => 1, }; if (grep /1/, map { !defined $hash->{$_} } qw(A B C)) { print "At least one undefined!\n"; } else { print "All there!\n"; }
This works, but I was curious if there was a better way. (At least, one that doesn't involve grep /1/, which just looks ugly to me...)

Replies are listed 'Best First'.
Re: Checking for defined-ness for a list
by japhy (Canon) on Jul 25, 2001 at 19:39 UTC
    Sure:
    %hash = ( A => 'that', B => 'those', C => 'groovy', D => 'artichoke', E => undef, F => 'flipping', ); @check = qw( A D F ); # change to 'E' or 'Z' to see if (@check == grep defined, @hash{@check}) { # ok }

    _____________________________________________________
    Jeff japhy Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re (tilly) 1: Checking for defined-ness for a list
by tilly (Archbishop) on Jul 25, 2001 at 19:36 UTC
    if (! grep defined($_), @hash{ qw(A B C) } ) { # etc }

    UPDATE
    merlyn is exactly right. Why you shouldn't try to answer with untested code while racing back to chatter...

      I think demorgan got the best of you here. This is "all are undefined", not "at least one undefined". You want:
      if (grep !defined($_), @hash{ qw(A B C) }) { # at least one undefined }

      -- Randal L. Schwartz, Perl hacker

Re: Checking for defined-ness for a list
by Hofmator (Curate) on Jul 25, 2001 at 19:39 UTC

    Use this: if (map { (defined $hash->{$_})? 1 : () } qw(A B C)) map in scalar context returns the number of list elements generated.

    Update: This is (probably) an inferior solution. I think it has to build the array in memory. I would use tilly's solution. But maybe map is implemented well and doesn't build the list when called in scalar context. Maybe someone with more knowledge than me could comment on that?

    Update 2: Another thought ... when testing for the definedness of lots of keys an explicit loop with exit might be quicker because you don't have to test all elements. It's enough when the first key is not defined. grep and map always go through the whole list.

    -- Hofmator

      Hofmator - How would you implement Update2 in a if-check? I like merlyn's solution, but I'm curious how to do it faster ...

        Well, I thought of something obvious along the lines of:

        my $undefined; while my $key (qw/A B C/) { $undefined = 1, last unless defined $hashref->{$key}; } if ($undefined) { # at least one key undefined } else { # all keys defined }
        I'd say this is faster than the grep/map approach when the list of keys to test is long. It also depends on the probability of a key being undefined. Try and Benchmark some real-life examples and you get a definitive answer concerning the speed.

        -- Hofmator

Re: Checking for defined-ness for a list
by runrig (Abbot) on Jul 25, 2001 at 21:51 UTC
    I think this is pretty straight forward:
    sub all_href_values_defined { my $href = shift; for (@_ ? @$href{@_} : values %$href) { return 0 unless defined; } return 1; }
Re: Checking for defined-ness for a list
by elbie (Curate) on Jul 25, 2001 at 22:58 UTC
    If you know that there are not going to be any extra keys, i.e. there are only going to be a combination of A, B and/or C but never D just count the nember of keys returned as follows:
    print "At least one undefined!\n" unless( scalar( keys %$hash ) == 3 ) +;
    Barring that, you might be able to sort out undesirebles from the list, or if you can sort such that you can guarantee the desired hashes are first, then just look at the element that would contain the last of the desirables:
    print "At least one undefined!\n" unless( ( sort { ...some function... + } keys %$hash )[3] eq 'C' );