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

Hello dear Monks!
I am trying to make the following script to work:
$prices{'pizza'} = 12.00; $prices{'coke'} = 1.25; $prices{'sandwich'} = 3.00; if (exists($prices{'cok'})) { print "found the key 'cok' in the hash\n"; }

but, it does not, since coke contains cok but it's not an exact match. Is this possible to do in a Perl hash?

Replies are listed 'Best First'.
Re: Check if key exist in hash but not exact match
by LanX (Saint) on Apr 03, 2023 at 18:30 UTC
    You need to parse all the keys for that.

    my @key_matches = grep { /cok/ } keys %prices

    NB: you can have multiple matches.

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

Re: Check if key exist in hash but not exact match
by choroba (Cardinal) on Apr 03, 2023 at 20:17 UTC
    If the string to be found is always a prefix, you can use a trie.
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; use experimental qw( signatures ); sub trie_exists($trie, $key) { my $ref = $trie; for my $char (split //, $key) { return unless exists $ref->{$char}; $ref = $ref->{$char}; } return 1 } my %prices = (pizza => 12, coke => 1.25, sandwich => 3); my %trie; for my $key (keys %prices) { my @chars = split //, $key; my $ref = \%trie; for my $char (@chars[0 .. $#chars - 1]) { $ref->{$char} //= {}; $ref = $ref->{$char}; } $ref->{ $chars[-1] } = $prices{$key}; } if (trie_exists(\%trie, 'cok')) { say "found the preifx 'cok' in the hash"; } die 'Full key' unless trie_exists(\%trie, 'pizza'); die 'Non prefix' if trie_exists(\%trie, 'andwich');

    There are also several trie modules on CPAN, but I haven't tried any of them, so can't make a recommendation.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Check if key exist in hash but not exact match - MCE::Shared::Hash
by marioroy (Prior) on Apr 04, 2023 at 23:55 UTC

    Various modules in MCE::Shared can be used directly (non-shared construction). Many classes MCE::Shared::{ Array, Cache, Hash, Minidb, and Ordhash } have query capabilities. The query logic lives in MCE::Shared::Base.

    See Syntax for Query String using MCE::Shared::Hash. Typically, the "keys" method returns all the keys. Here, a query string is specified to return keys that match a regular expression. Subsequently, depending on the price.

    use strict; use warnings; use MCE::Shared::Hash; my $prices = MCE::Shared::Hash->new(); $prices->{'pizza'} = 12.00; $prices->{'coke'} = 1.25; $prices->{'sandwich'} = 3.00; if ($prices->keys("key =~ /cok/")) { print "found the key 'cok' in the hash\n"; } if ($prices->keys("key =~ /cok/ :AND val >= 1.50")) { print "found the key 'cok' in the hash greater than or equal \$1.50\ +n"; } if ($prices->keys("key =~ /cok/ :AND val < 1.50")) { print "found the key 'cok' in the hash less than \$1.50\n"; } __END__ found the key 'cok' in the hash found the key 'cok' in the hash less than $1.50

    Q. Why were query capabilities added to MCE::Shared classes?

    A. In the context of sharing, the query mechanism is beneficial for the shared-manager process. It is able to perform the query where the data resides versus client-process(es) iterating involving lots of IPC.

Re: Check if key exist in hash but not exact match
by Anonymous Monk on Apr 03, 2023 at 21:00 UTC

    See Tie::Hash::Regex, and, if not exactly what you need, its "See also" section, and "See also" again from there, etc. I remember it was fun playing with re::engine::TRE, it may be amusing to cross-breed this engine and 1st link.

Re: Check if key exist in hash but not exact match
by LanX (Saint) on Apr 03, 2023 at 22:05 UTC
    TIMTOWTDI:
    use v5.12.0; use warnings; # Check if key exist in hash but not exact match # https://perlmonks.org/?node_id=11151448 my %prices = ("coke" => 1.25, "sandwich" => 3, "pizza" => 12); my $concat = join $; => keys %prices; for my $pat (qw/cok oke ok a/) { my @keys = $concat =~ m/([^$;]*${pat}[^$;]*)/g; say "--- pattern: /$pat/"; say "$_ => $prices{$_}" for @keys; say "."; }

    --- pattern: /cok/ coke => 1.25 . --- pattern: /oke/ coke => 1.25 . --- pattern: /ok/ coke => 1.25 . --- pattern: /a/ sandwich => 3 pizza => 12 .

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

Re: Check if key exist in hash but not exact match
by kcott (Archbishop) on Apr 04, 2023 at 15:41 UTC

    TMTOWTDI (but also just for a bit of fun and to give Perl v5.36 an outing).

    #!/usr/bin/env perl use v5.36; use List::Util 'max'; my %price = ( 'Coca Cola' => 1.25, coke => 1.25, cola => 1.25, 'Pepsi Cola' => 1.25, pizza => 12.00, sandwich => 3.00, 'Undead Cola' => undef, ); say 'Enter whole or partial name for key (Enter to exit)'; while (1) { print "\nName: "; my $name = <STDIN>; chomp $name; unless (length $name) { say 'Exiting ...'; last; } unless ($name =~ /^[A-Za-z0-9 ]+$/) { say 'Only alphanumeric+space name searches allowed.'; next; } check_keys(\%price, $name); } sub check_keys ($hash, $name) { my $fmt = "%-7s %-@{[max map length, keys %$hash]}s %s\n"; my @matches = grep /$name/i, keys %$hash; if (@matches) { printf $fmt, qw{Match Key Value}; printf $fmt, qw{----- --- -----}; if (exists $hash->{$name}) { @matches = grep !/^$name$/, @matches; printf $fmt, 'EXACT', $name, $hash->{$name} // '<undefined +>'; } if (@matches) { for my $match (sort @matches) { printf $fmt, 'PARTIAL', $match, $hash->{$match} // '<u +ndefined>'; } } } else { say "No keys match '$name'."; } return; }

    Sample run:

    Enter whole or partial name for key (Enter to exit) Name: cok Match Key Value ----- --- ----- PARTIAL coke 1.25 Name: col Match Key Value ----- --- ----- PARTIAL Coca Cola 1.25 PARTIAL Pepsi Cola 1.25 PARTIAL Undead Cola <undefined> PARTIAL cola 1.25 Name: Coca Cola Match Key Value ----- --- ----- EXACT Coca Cola 1.25 Name: Undead Cola Match Key Value ----- --- ----- EXACT Undead Cola <undefined> Name: Cola Match Key Value ----- --- ----- PARTIAL Coca Cola 1.25 PARTIAL Pepsi Cola 1.25 PARTIAL Undead Cola <undefined> PARTIAL cola 1.25 Name: cola Match Key Value ----- --- ----- EXACT cola 1.25 PARTIAL Coca Cola 1.25 PARTIAL Pepsi Cola 1.25 PARTIAL Undead Cola <undefined> Name: ^cola$ Only alphanumeric+space name searches allowed. Name: (?{/path/to/evil_code}) Only alphanumeric+space name searches allowed. Name: Nightingale Tongues in Aspic No keys match 'Nightingale Tongues in Aspic'. Name: Exiting ...

    Note: Code injection (e.g. via '(?{ code })') is avoided by only allowing the search criterion to contain alphanumeric characters and spaces. Depending on the actual keys in the production code, a different mechanism to achieve this may be required.

    — Ken

      I tinkered with my previous code (Re: Check if key exist in hash but not exact match) and made a couple of improvements.

      1. The potentially ambiguous "Enter whole or partial name for key (Enter to exit)" is now "Enter whole or partial name for key (just hit Return to exit).".
      2. Where a match only differs in case, it is shown as NO_CASE instead of PARTIAL; for example, Cola matches cola if case is ignored:
        Name: Cola Match Key Value ----- --- ----- NO_CASE cola 1.25 PARTIAL Coca Cola 1.25 PARTIAL Pepsi Cola 1.25 PARTIAL Undead Cola <undefined>

      As there are only a few code changes, and the sample run is the same as before except for the Name: Cola (shown above), I've put all of this in a spoiler (view at your leisure).

      — Ken

        I tinkered some more:

        There was a bug which I've fixed. I've no idea why, but I'd coded for only one NO_CASE. Obviously, you can have ABC, Abc, abc, AbC, and so on.

        I decided that I was doing too much work with @matches. I changed that to %matches and, instead of removing elements from the array by recreating it using grep, I'm simply using delete to remove key/value pairs. This resulted in much cleaner code and, I imagine, would be a lot more efficient.

        There was also an interesting point regarding the contents of the "Value" column produced by printf. Having introduced undef values in my test data for basic edge-case checks, this meant that there was either a price (number) or '<undefined>' (string). A %s worked fine for both when the price was 1.25; however, a price of 3.00 was converted to just 3.

        My introduction of undef may have been completely artificial and would never occur in the OP's data: in this case, a %.2f would probably suffice throughout. I've left the undef values as it was an interesting exercise dealing with this. I can see potential improvements here but won't go into that further unless it is something the OP really wants.

        As before, I'll put the new code and another sample run in a spoiler.

        — Ken

Re: Check if key exist in hash but not exact match
by tybalt89 (Monsignor) on Apr 03, 2023 at 21:15 UTC

    Maybe like this:

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11151448 use warnings; my %prices; $prices{'pizza'} = 12.00; $prices{'coke'} = 1.25; $prices{'sandwich'} = 3.00; for my $key ( sort keys %prices ) { $key =~ m<(.+)(?{ $prices{$1} //= $prices{$key} })(*FAIL)>; } #use Data::Dump 'dd'; dd \%prices; if (exists($prices{'cok'})) { print "found the key 'cok' in the hash\n"; }

    Outputs:

    found the key 'cok' in the hash
Re: Check if key exist in hash but not exact match
by Tux (Canon) on Apr 04, 2023 at 09:52 UTC

    Maybe a bit overengineered, but I think this comes close to what you want:

    -->

    The price for a coke is 1.25 No full match for product cok The price for a coke is 1.25 No full match for product cola The price for a coke is 1.25 No full match for product sandwitch The price for a sandwich is 3

    Enjoy, Have FUN! H.Merijn