9

I have a list of strings list. I am trying to get rid of some elements that match string pattern patt:

list /. x_ /; StringMatchQ[x, patt] -> Nothing

I get the result I want but I also get an error:

StringMatchQ::strse: String or list of strings expected at position 1 in StringMatchQ[List, patt].

This happens because ReplaceAll also evaluates the condition on the Head of list. How can I get the result I want but prevent the error?

amrods
  • 637
  • 3
  • 13

5 Answers5

7

IMHO the simplest solution is to restrict the pattern on the left side of Condition, i.e. change x_ to x_String:

patt = ___ ~~ "a" ~~ ___;

{"good", "bad"} /. x_String /; StringMatchQ[x, patt] -> Sequence[]

(*  {"good"}  *)

You can instead use Replace as indicated in other answers, however:

  • the optimal levelspec to target atomic elements like Strings is {-1}
  • The default Heads value False is what prevents List from being pattern matched.

Please understand that Replace does not work the same as ReplaceAll, regardless of levelspec, because it uses a different traversal order. See:

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
6

Use Replace instead of ReplaceAll.

ReplaceAll[{"x", "y", "z"}, x_ /; StringMatchQ[x, "x"] -> Nothing]

StringMatchQ::strse: String or list of strings expected at position 1 in StringMatchQ[List,x].

(* {"y", "z"} *)

as in the question. In contrast,

Replace[{"x", "y", "z"}, x_ /; StringMatchQ[x, "x"] -> Nothing, {1}]

yields the same answer but without the error message.

Correction

For nested Lists, it appears necessary to restrict patt to be a String, as noted by Mr.Wizard.

Replace[{"x", "y", "z", {"a", "x", {"b", "x"}}}, 
    x_String /; StringMatchQ[x, "x"] -> Nothing, {1, Infinity}]

(* {"y", "z", {"a", {"b"}}} *)
bbgodfrey
  • 61,439
  • 17
  • 89
  • 156
6

Solution

You could use Replace (ReplaceAll is effectively the same as using All or {0, Infinity} as levelspec in Replace).

Example

list = {"a", "b", "c"};
patt = "a";
Replace[list, x_ /; StringMatchQ[x, patt] -> Nothing, {1, Infinity}]

(* {"b", "c"} *)

Alternative Solution

In your case, since you are replacing something with Nothing, you could simply use DeleteCases:

DeleteCases[list, patt, All]

(* {"b", "c"} *)
JungHwan Min
  • 4,664
  • 1
  • 22
  • 36
1
list = {"x", "y", "z", "x"};

Showing some of the newer functions

ReplaceAt (new in 13.1)

ReplaceAt[_ :> Nothing, Position[list, "x"]] @ list

{"y", "z"}

SequenceSplit (new in 11.3)

First @ SequenceSplit[list, {"x"}]

{"y", "z"}

SequenceReplace (new in 11.3)

SequenceReplace[list, {"x"} :> Nothing]

{"y", "z"}

DeleteElements (new in 13.1)

DeleteElements[list, {"x"}]

{"y", "z"}

The above functions are rather slow when applied to large lists.

eldo
  • 67,911
  • 5
  • 60
  • 168
1

An alternative is to use StringCases and Except:

list = {"x", "y", "z", "x"};

Catenate@StringCases[list, {Except["x"]}]

({"y", "z"})

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44