13
$\begingroup$

Consider the following example:

Suppose I am interested in words that contain all the letter of the list characters={"e","t","l","a","b"} at least once. An example would be the word "abilities" or a more minimalist word would be "table".

If I try to use a general regular expression language then I can achieve this by:

DictionaryLookup[RegularExpression[StringJoin["(?=.*"<>#<>")"&/@characters,".*"]],IgnoreCase->True]

But I want to do this using StringPatterns only. Is there a way to do this?

Since I can't find the lookahead assertions in StringPatterns I try the following:

DictionaryLookup@PatternAnd[___~~"e"~~___,___~~"t"~~___,___~~"l"~~___,___~~"a"~~___,___~~"b"~~___]

But I can't find a PatternAnd that would work here. Does Wolfram language has such a pattern construct? I see there is AnyOrder which I try to use:

DictionaryLookup[___~~AnyOrder["e",___,"t",___,"l",___,"a",___,"b"]~~___]

It takes forever and doesn't end. Can someone help me to find a StringPattern that would achieve the result of the regex?

$\endgroup$
5
  • $\begingroup$ how could "abilities" come from list of chars when i is not even there? I deleted my answer until I understand better what is it you are asking. $\endgroup$
    – Nasser
    Apr 20 '20 at 20:16
  • $\begingroup$ @Nasser word must contain all the letters in the list at least once but may also contain any letter outside the list. $\endgroup$
    – user13892
    Apr 20 '20 at 20:36
  • $\begingroup$ I see. when you say "any other letter." do you mean any ONE other letter, or can it contain more multiple number of other letters? $\endgroup$
    – Nasser
    Apr 20 '20 at 20:38
  • $\begingroup$ Once the condition of letters in the list is satisfied, there is no restriction at all. It can have any number of letters any number of times (just try to match the output of regex command). $\endgroup$
    – user13892
    Apr 20 '20 at 20:41
  • $\begingroup$ Related to Daily Challenge (Day 9) here. $\endgroup$ Apr 21 '20 at 2:39
12
$\begingroup$

The language has StringContainsQ and ContainsAll, it seems we need a combination of the two:

StringContainsAll[string_String, patts_List, opts : OptionsPattern[]] := 
   AllTrue[patts, StringContainsQ[string, #, opts] &];
StringContainsAll[patts_List, opts : OptionsPattern[]][x_] := 
    StringContainsAll[x, patts, opts]

Now you can get the word list with

Select[DictionaryLookup[], 
 StringContainsAll[{"e", "t", "l", "a", "b"}, IgnoreCase -> True]]

or, if you want a pattern test,

DictionaryLookup[
 StartOfString ~~ x__ ~~ EndOfString /; (StringContainsAll[x, {"e", "t", "l", "a", "b"}, 
    IgnoreCase -> True])]

edit: You can this function in the repository as ResourceFunction["StringContainsAll"]

$\endgroup$
7
$\begingroup$

This only makes minimal use of string patterns (as with Jason B.'s answer, it builds on StringContainsQ), but it uses Fold in a way I don't think I've ever seen it used before, to get the elements of a list that satisfy a bunch of predicates. It is terribly efficient?

Maybe not. But I still think it's kind of cool:

Fold[Select, DictionaryLookup[], StringContainsQ /@ characters]
(* {aberrational, abhorrently, abilities, <<1071>>, worktable, worktables, writable} *)
$\endgroup$
1
  • 1
    $\begingroup$ +1 for innovative use of Fold! $\endgroup$
    – Victor K.
    Apr 21 '20 at 3:22
2
$\begingroup$

Instead of a string patterns, how about set manipulation:

contains[word_, characters_] := Module[{chars, wordChars},
  wordChars = Union@Characters@word;
  chars = Union@characters;
  Intersection[chars, wordChars] == chars
  ]

contains["abilities", {"e", "t", "l", "a", "b"}]

(* True *)

contains["ability", {"e", "t", "l", "a", "b"}]

(* False *)

Although I do see your point -- you really want to do this with string syntax.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.