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

Can you suggest code to determine whether a string contains all of a list of characters in any order? Thanks.

Replies are listed 'Best First'.
Re: string containing characters
by GrandFather (Saint) on Jan 21, 2025 at 06:22 UTC

    What have you tried? Maybe you should show us some sample code?

    use strict; use warnings; my @wanted = (1, 2, 3, 4); for my $test ('1432', '1345', 'brown fox') { my $count = grep {$test =~ /$_/} @wanted; if ($count == @wanted) { print "'$test' contains all wanted characters\n"; } elsif ($count) { print "'$test' does not contain all wanted characters\n"; } else { print "'$test' contains no wanted characters\n"; } }

    Prints:

    '1432' contains all wanted characters '1345' does not contain all wanted characters 'brown fox' contains no wanted characters

    Which contains some deliberate errors and should not be used for "production" code.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Or, without a regex:
      ... my $count = grep { -1 != index $test, $_ } @wanted; ...
      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      Note: Possibly ok, but doesn't handle duplicates in @wanted.

        Yes. The key deliberate

        should not be used for "production"
        issue. That might or might not be an issue for the OP, depending on how the test set of characters is generated. As always, with better information we can give better answers.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: string containing characters
by hippo (Archbishop) on Jan 21, 2025 at 09:17 UTC

    TIMTOWTDI.

    use strict; use warnings; use Test::More tests => 2; my $goodstr = 'Can you suggest code to determine whether a string cont +ains all of a list of characters in any order?'; my $badstr = 'How to ask better questions using Test::More and sample +data'; my @list = qw/? s a y/; ok haschars ($goodstr, @list), 'Match'; ok !haschars ($badstr, @list), 'No match'; sub haschars { my $str = shift; for my $c (@_) { return 0 if -1 == index $str, $c; } return 1; }

    See also How to ask better questions using Test::More and sample data.


    🦛

      Note: Possibly ok, but doesn't handle duplicates in @list.

        Handle how? If you try it with my @list = qw/? ? ? s s s a a a y y y/; instead, it works equally well. Not as efficiently, I grant you but it doesn't croak or give the wrong result.


        🦛

Re: string containing characters
by tybalt89 (Monsignor) on Jan 21, 2025 at 15:17 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11163777 use warnings; my @wanted = (1, 2, 3, 4); my $pattern = join '', map "(?=.*\Q$_\E)", @wanted; for my $test ('1432', '1345', 'brown fox', '9876?54321') { printf "%20s => %s\n", $test, $test =~ /^$pattern/s ? "has all" : "does not have all"; }

    Outputs:

    1432 => has all 1345 => does not have all brown fox => does not have all 9876?54321 => has all

      Note: Possibly ok, but doesn't handle duplicates in @wanted. (Looking for two x would be done by building (?=.*x.*x).)

      The advantage of this way is that you can check a large number of strings against one list efficiently (by adding my $re = /^$pattern/s and using $re in the loop).

Re: string containing characters
by tybalt89 (Monsignor) on Jan 22, 2025 at 20:07 UTC

    Here's a couple more while awaiting a ruling on duplicates...

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11163777 use warnings; my @wanted = (1, 2, 3, 4, 2); my $pattern = join '.*', map quotemeta, sort @wanted; for my $test ('1432', '21432', '1345', 'brown fox', '98276?54321') { printf "%20s => %s\n", $test, (join '', sort split //, $test) =~ /$pattern/s ? "has all" : "does not have all"; } print "\n"; sub hasall # similar to what I did in https://perlmonks.org/?node_id=1 +1108516 { my ($have, @want) = @_; $have =~ s/\Q$_// or return 0 for @want; return 1; } for my $test ('1432', '21432', '1345', 'brown fox', '98276?54321') { printf "%20s => %s\n", $test, hasall( $test, @wanted ) ? "has all" : "does not have all"; }

    Outputs:

    1432 => does not have all 21432 => has all 1345 => does not have all brown fox => does not have all 98276?54321 => has all 1432 => does not have all 21432 => has all 1345 => does not have all brown fox => does not have all 98276?54321 => has all
Re: string containing characters
by harangzsolt33 (Deacon) on Jan 21, 2025 at 06:38 UTC
    I would use a regex to eliminate characters from one string that appear in another. If that one string becomes empty at the end, then we know that all the characters in it occurred in the other string. So, I use this logic to decide whether the string matches all characters or not. In the following example, the "Hello World" string contains all of these characters: o r e H

    What this little program does not do is that it does not count how many letters are matched. So, if you want to know if a string contains at least 5 letter A's and 3 B's and 2 C's, then this program is not going to work...

    #!/usr/bin/perl -w use strict; use warnings; my $SAMPLE = "Hello World\r\n"; my $MATCHALL = "oreH"; $MATCHALL =~ s/[\Q$SAMPLE\E]+//g; if (length($MATCHALL) == 0) { print "\nCONTAINS ALL THE CHARACTERS\n"; } else { print "\nDOES NOT CONTAIN ALL THE CHARACTERS\n"; } exit;

      Note: Possibly ok, but doesn't handle duplicates in $MATCHALL

        Yeah, that's true. But we could turn it around and then it works:

        #!/usr/bin/perl -w use strict; use warnings; my $SAMPLE = "Hello World\r\n"; my $MATCHALL = "ooreH\r\n"; my $SAMPLE_LENGTH = length($SAMPLE); $SAMPLE =~ s/[\Q$MATCHALL\E]{1}//g; if (length($SAMPLE) == $SAMPLE_LENGTH - length($MATCHALL)) { print "\nCONTAINS ALL THE CHARACTERS\n"; } else { print "\nDOES NOT CONTAIN ALL THE CHARACTERS\n"; } exit;