5
$\begingroup$

Suppose I have myStrList, a list of one-character strings:

myStrList = {"A", "a", "B", "b"};

I wish to create a function that will return True if a string is either "A" or "a", and False otherwise. I wish to Map this function across the list myStrList.

  1. Method (1). One way to do this is to use MemberQ to test if a string is a member (element) of the list {"A", "a"}. This method gives the expected output:
(* Method (1): using MemberQ *)
Map[MemberQ[{"A", "a"}, #] &, myStrList]
(* {True, True, False, False} *)
  1. Method (2). Another way is to use MatchQ with a pattern object involving Alternatives (or, equivalently, |). This method also gives the expected output:
(* Method (2): using MatchQ and Alternatives (|) *)
Map[MatchQ[#, Alternatives["A", "a"]] &, myStrList]
Map[MatchQ[#, "A" | "a"] &, myStrList]
(* {True, True, False, False} *)
(* {True, True, False, False} *)
  1. Method (3). Still another way is to use Or (or, equivalently, ||), the logical OR. Again, this method gives the expected output:
(* Method (3): using Or (||) *)
Map[Or[# == "A", # == "a"] &, myStrList]
Map[# == "A" || # == "a" &, myStrList]
(* {True, True, False, False} *)
(* {True, True, False, False} *)

Is there a way to condense method (3), so that it is less verbose? Instead of asking whether # == "A" OR # == "a", can I ask whether # == "A" OR "a"? I tried the following:

Map[# == Or["A", "a"] &, myStrList]
Map[# == ("A" || "a") &, myStrList]
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)

Neither of the above gives the expected output. It looks as though the equality test is being held. I notice that Or (or, equivalently, ||) has attribute HoldAll:

Attributes[Or]
(* {Flat, HoldAll, OneIdentity, Protected} *)

Could it be that I need to use Evaluate to force the evaluation of # == ("A" || "a")? It doesn't seem that Evaluate helps in this regard:

Map[Evaluate[# == Or["A", "a"]] &, myStrList]
Map[Evaluate[# == ("A" || "a")] &, myStrList]
Map[# == Evaluate[Or["A", "a"]] &, myStrList]
Map[# == Evaluate[("A" || "a")] &, myStrList]
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)

Nor does it seem that using Simplify or FullSimplify will evaluate the expressions:

Simplify /@ {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")}
FullSimplify /@ {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")}
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)
(* {"A" == ("A" || "a"), "a" == ("A" || "a"),
  "B" == ("A" || "a"), "b" == ("A" || "a")} *)

The solution to this may be obvious, but it's not obvious to me. How can I force testing expressions like "A" == ("A" || "a") to evaluate? Or where am I going wrong conceptually?

$\endgroup$
8
  • 3
    $\begingroup$ # == Or["A", "a"] & does not look right to me. The syntax of Or is not like this. But why not just use something like AnyTrue[myStrList,Or[#=="A",#=="a"]&]. I know you said you wanted to use Map. In Mathematica there are many ways to do the same thing. I see nothing wrong with your earlier tries also. But the last one, your Or usage is not correct. You can fix it by doing Or[# == "A", # == "a"] i.e. move the # inside. $\endgroup$
    – Nasser
    Commented Nov 8 at 21:12
  • 4
    $\begingroup$ StringMatchQ["A" | "a"]@myStrList ? $\endgroup$
    – Syed
    Commented Nov 9 at 1:59
  • 3
    $\begingroup$ Or, ToUpperCase[#] == "A" & /@ myStrList $\endgroup$
    – Bob Hanlon
    Commented Nov 9 at 2:12
  • 2
    $\begingroup$ @Nasser's suggestion of AnyTrue[] will "create a function that will return True..." as you say you want. Further, it will return True as soon as it runs into an "a" or an "A", whereas Map[] will pointlessly continue to check all remaining strings. $\endgroup$
    – Michael E2
    Commented Nov 9 at 13:42
  • 2
    $\begingroup$ Or[] is not way represent alternative values. It's a logical connective to connect boolean expressions (whose value is presumed to be from the set {True, False}). Since a string does not represent a boolean expression, Mathematica does not know what to do with it and leaves it alone. About the only evaluation one can get is ("a" || "A") == ("A" || "a") since each side is identical. Now Or could connect to equalities, as in Thread[# == ("A" || "a"), Or] & /@ myStrList. $\endgroup$
    – Michael E2
    Commented Nov 9 at 13:53

5 Answers 5

4
$\begingroup$

StringMatchQ works with regular expressions or Mathematica string expressions.

myStrList = {"A", "a", "B", "b"};
(* this regular expression matches any of the characters in square brackets *)
StringMatchQ[myStrList, RegularExpression["[Aa]"]]
(* (?i) is an option to match while ignoring case *)
StringMatchQ[myStrList, RegularExpression["(?i)A"]]
(* the IgnoreCase option works the same way *)
StringMatchQ[myStrList, RegularExpression["A"], IgnoreCase->True]
(* use string expression to match a list of characters *)
StringMatchQ[myStrList, "A"|"a"]
StringMatchQ[myStrList, Alternatives@@{"A", "a"}]
(* ... and IgnoreCase *)
StringMatchQ[myStrList, "A", IgnoreCase -> True]

The result is the same for each example.

{True, True, False, False}

The same match expressions work with StringCases and StringReplace so it's a flexible method for other problems.

StringCases[myStrList, RegularExpression["[Aa]"]]
{{"A"}, {"a"}, {""}, {""}}
StringReplace[myStrList, RegularExpression["[Aa]"] -> "X"]
{{"X"}, {"X"}, {"B"}, {"b"}}
$\endgroup$
6
$\begingroup$

Conceptually, the best way to quickly determine if an element is in a set is by using a HashSet:

set = CreateDataStructure["HashSet", {"A", "a"}]
set["MemberQ", #] & /@ {"A", "a", "B", "b"}
$\endgroup$
5
$\begingroup$
myStrList = {"A", "a", "B", "b"};
Or @@ Thread[# == {"A", "a"}] & /@ myStrList

{True, True, False, False}
$\endgroup$
4
  • $\begingroup$ Thread[# == Or["A", "a"], Or] & /@ myStrList also works. $\endgroup$ Commented Nov 12 at 10:41
  • $\begingroup$ @SjoerdSmit Yes, but my code is by a tiny bit shorter :-) $\endgroup$ Commented Nov 12 at 10:45
  • $\begingroup$ Not true, it's actually the same number of characters. And my code can be 2 chars shorter as well: Thread[# == "A" || "a", Or] & $\endgroup$ Commented Nov 12 at 10:48
  • $\begingroup$ @SjoerdSmit Mine StringLength["Or@@Thread[#=={\"A\",\"a\"}]&/@myStrList"] == 36 Yours StringLength["Thread[#==Or[\"A\",\"a\"],Or]&/@myStrList"] == 37. The code in your last comment does not work :-) $\endgroup$ Commented Nov 12 at 10:57
5
$\begingroup$

Here is a way to live dangerously by using $Pre:

$Pre = Function[# /. (x_ == HoldPattern[Or[s__String]]) :> 
  (Or @@ (EqualTo[x] /@ {s}))];

Map[# == ("A" || "a") &, myStrList]
(* {True, True, False, False} *)
$\endgroup$
5
$\begingroup$

You can define a listable function and use it without Map:

f["a" | "A"] = True;
f[_] = False;
Attributes[f] = {Listable};

myStrList = {"A", "a", "B", "b"};
f[myStrList]
(*    {True, True, False, False}    *)

Or you could use a Dispatch:

d = Dispatch[{("A" | "a") -> True, _String -> False}];

myStrList /. d
(*    {True, True, False, False}    *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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