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

Hi, I'm a novice at Perl. I am looking for help in this scenario: I want to see if a match or partial match of a $sting is found within an array. If the String = "2cat6" and the words "cat" and "catch" are both found within the array, I want to be able to get a 'true' when either word is found within the array in this case. I have tried to use m// as well as grep but if the string isn't the exact same word in the string and array it won't be found. So where $string = "2cat6" and "cat" is within the array it won't find "cat". But if $string = "catch" and "catch" is within the array it will find it but if "cat" was in the array it would not pick "cat" out of the string. All of my strings in this event are 5 letter and or number combinations in a random selection. Sometimes when the string is created some bad words are formed and I want to avoid that. Hopefully someone will help me with this.

Replies are listed 'Best First'.
Re: Finding a partial match
by hippo (Archbishop) on May 07, 2023 at 08:51 UTC
    I have tried to use m// as well as grep but if the string isn't the exact same word in the string and array it won't be found.

    If you have code which runs but doesn't do quite what you want then it would be very useful if you were to provide that here in the form of an SSCCE - that way we can be sure we know what it is you are trying to achieve. The verbose description is OK but I'm finding it hard to understand quite what you have and quite what you want it to do.

    I think you want to treat the array items as substrings for the purposes of matching against "the string". So, here is a test script to get you started in that vein.

    #!/usr/bin/env perl use strict; use warnings; use Test::More tests => 2; my @x = qw/catch cat foo nocat/; my $str = '2cat6'; my @found = grep { -1 < index $str, $_ } @x; my @want = qw/cat/; is_deeply \@found, \@want, "Found '@found' in '$str'"; $str = '2catch6'; @found = grep { -1 < index $str, $_ } @x; @want = qw/catch cat/; is_deeply \@found, \@want, "Found '@found' in '$str'";

    This code uses Test::More to run 2 specific tests. In each one, the desired outcome is held in @want and this is compared with the generated array of matches stored in @found. If these aren't the right tests then you can amend the script as you see fit. See How to ask better questions using Test::More and sample data for more on using tests in this way.


    🦛

Re: Finding a partial match
by kcott (Archbishop) on May 07, 2023 at 05:56 UTC

    G'day jpys,

    This does what you asked for:

    #!/usr/bin/env perl use strict; use warnings; my $string = '2cat6'; my @chars = split //, $string; my @tests = qw{2cat6 cat catch vwxyz 12345 67890 13457}; TEST: for my $test (@tests) { for my $char (@chars) { if (0 <= index $test, $char) { print "$test: true\n"; next TEST; } } print "$test: false\n"; }

    Output:

    2cat6: true cat: true catch: true vwxyz: false 12345: true 67890: true 13457: false

    — Ken

      This does what you asked for

      It does what the OP appears to ask for, but probably in more extreme form than they want - the actual problem they are trying to solve is that "Sometimes when the string is created some bad words are formed and I want to avoid that". So if they test against a list of bad words, this will reject any string sharing even a single character with any of the bad words - potentially rejecting absolutely every string, if the list of bad words is extensive enough.

      For this context, one possibility (and one I would guess at) is that the OP wants to match not every length-1 substring of the bad words, but only those substrings that look close enough to the word that they bring it to mind. Assume "LACK" is a bad word: we might think that "LAC" and "LAK" are both close enough to bring it to mind, but that "ACK" is not. In that case a better solution might be to put "LAC" and "LAK" in the naughty list and reject only complete matches.

      At this stage, the OP is best served by helping them find the right questions to ask to clarify their own thoughts about what they want to achieve.

        I rather suspected that the OP would come back with additions to his spec: perhaps a minimum substring length; perhaps something else. Of course, he might simply adapt my code to count three consecutive matched characters, or whatever else he might want. I believed I was clear when I wrote: "This does what you asked for"; but maybe not.

        I did note the penultimate sentence about "bad words". It seemed to have no bearing on the relatively lengthy problem description which preceded it. It certainly didn't strike me as the "actual problem".

        Anyway, that's an interesting take; let's wait to see what the OP says.

        — Ken

Re: Finding a partial match
by LanX (Saint) on May 07, 2023 at 11:55 UTC
Re: Finding a partial match
by WithABeard (Beadle) on May 08, 2023 at 13:36 UTC

    If I have understood correctly; you are generating random strings of 5 characters, and you want to detect if any of the bad words you have in an array is contained within these random strings.

    Here is a little script that does something to that effect:

    use feature 'state'; my @bad_words = qw< poop p0op po0p p00p kill k1ll boink bo1nk b0ink b01nk form shut test kiss urge lack sigh find long wear tend nod try fit face care jump feel show hide slip lay wash tie move give omit sum use pay work rule hear fine view mind fund bury stir hunt vary play eat die time bend owe tap meet see nip penis pain >; while(1) { my $string = random_string(); my @matches = grep { -1 < index "\L$string", $_ } @bad_words; print "$string is bad (@matches)\n" if @matches; } sub random_string { state @allowed = ('a'..'z', 'A'..'Z', 0..9); my $string = ''; $string.= $allowed[int rand @allowed] while( 5 > length $string ); return $string; }
Re: Finding a partial match
by harangzsolt33 (Deacon) on May 07, 2023 at 13:10 UTC
    This sounds like a great programming exercise while I drink my morning coffee...

    #!/usr/bin/perl use strict; use warnings; my $string = '2raccoon49-racing'; my @tests = qw(Winnie Pooh racat Bats xyz35 Barack ccoons acc 2ra); print "\n\nOur search string is : \"$string\"\n\nMinimum number of let +ters that must match.\n|\n|\nV"; foreach (@tests) { print "\t$_"; } for (my $N = 1; $N < 7; $N++) { print "\n$N"; foreach (@tests) { print "\t", PartialMatchStr($string, $_, $N); } } print "\n\n"; ################################################## # This function performs a partial match and returns 1 # if at least N bytes of string1 and string2 match # anywhere. Returns zero otherwise. # # Usage: INTEGER = PartialMatchStr(STR1, STR2, N) # sub PartialMatchStr { @_ > 2 or return 0; foreach (@_) { defined $_ or return 0; } my $N = $_[2]; my $L = length($_[1]) - $N; if (length($_[0]) < $N || $L < 0) { return 0; } # Impossible for (my $i = 0; $i <= $L; $i++) { # Take one segment from STR1 and find it within STR2. if ( index($_[0], substr($_[1], $i, $N) ) >= 0) { return 1; } } return 0; } ##################################################