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

I wanted to check if any of a set of keys existed in the hash, but was surprised to find that exists will only do a single key lookup. This seems like something that should be in core not just because it is syntax sugar, but because it is would be much more efficient to have the internal C code perform the multiple lookups. Example:
my %haystack; @haystack{'aa'..'ff'} = (); # syntax error: say exists @haystack{qw(aa bb cc dd)}; # need to use this instead: use List::Util qw(first); say !! first { exists $haystack{$_} } qw(aa bb cc dd);

Replies are listed 'Best First'.
Re: Why doesn't exist work with hash slices?
by duelafn (Parson) on Sep 21, 2025 at 12:30 UTC

    It isn't obvious that exists @haystack{qw(aa bb cc dd)} should provide an "ANY" test. Some might think an "ALL" test would be correct. Thus, it definitely doesn't belong in core.

    I'd suggest using any for clarity and to remove the need for !! (and the risk that one of your keys might be "0").

    use List::Util qw(any first); my %haystack; @haystack{'aa'..'ff',"0"} = (); say any { exists $haystack{$_} } qw(aa bb cc dd); say any { exists $haystack{$_} } qw(zz 0); # OK say !! first { exists $haystack{$_} } qw(zz 0); # Oops!

    Good Day,
        Dean

      > exists @haystack{qw(aa bb cc dd)} should provide an "ANY" test. Some might think an "ALL" test would be correct.

      In order to respect orthogonality to delete I'd expect a _list_¹ of booleans to be returned.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      see Wikisyntax for the Monastery

      ¹) emphasize updated

        According to perldoc -f delete, it returns the deleted value, not a Boolean. And,

        $ perl -E 'my %foo = qw{ bar 42 }; say delete $foo{bar};'
        

        in fact prints 42.

Re: Why doesn't exist work with hash slices?
by LanX (Saint) on Sep 20, 2025 at 22:29 UTC
    Technically because a slice returns a list of values not keys.

    Something like exists %haystack{@slice} might be a workaround

    Also the semantic with first that you propose isn't intuitive for me.

    But

    • @vector = map { exists $haystack{$_} } qw(aa bb cc dd)

    would be the obvious choice and probably wouldn't merit magic sugar.¹

    If anything this might be an argument for hyper operator syntax, which could be combined with various simple operators like exists.

    Finally your goal might be possibly achieved with aliasing², I'll test and update here if it's possible.

    updates

    In hindsight: This is all semantically very close to calculating a set intersection, but alas not easy in Perl.

    ¹) though there is a precedent with delete @hash{@slice} which returns a list

    ²) no, doesn't work an exists $_ doesn't compile

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

Re: Why doesn't exist work with hash slices?
by ikegami (Patriarch) on Sep 21, 2025 at 16:12 UTC

    What would it even mean? Should it check if they all exist, or if any exist?

    And that's why it doesn't exist. It's not clear what that should do, it's not clear what it does by looking at it, it's not something that's commonly needed, and it's easily done with slightly more code.

    If you want to check if all of the elements exist, you can use

    all { exists( $h{ $_ } ) } @keys

    If you want to check if any of the elements exist, you can use

    any { exists( $h{ $_ } ) } @keys
      These are even efficient, with 5.42's experimental any and all operators. Before those, I'd almost never use List::Util's any/all/first, unless readability is super important or the body is doing something expensive like I/O, preferring something like:
      0 != grep exists $hash{$_}, @keys # any 0 == grep ! exists $hash{$_}, @keys # all

        While the new functions in core are a lot faster still, at least for any, List::Util has been way faster than grep.

        use strict; use warnings; use v5.42; use feature 'keyword_any'; no warnings 'experimental::keyword_any'; use List::Util; use Benchmark 'cmpthese'; my @list = 0 .. 1_000_000; my %hash = map { $_ => 1 } @list; cmpthese(-5, { 'grep' => sub { return 1 if 0 != grep exists $hash{$_}, @list }, 'List::Util::any' => sub { return 1 if List::Util::any { exists $ +hash{$_} } @list }, 'core any' => sub { return 1 if any { exists $hash{$_} } @list }, });
        Rate grep List::Util::any core any grep 14.1/s -- -98% -99% List::Util::any 634/s 4412% -- -46% core any 1168/s 8209% 84% --


        The way forward always starts with a minimal test.