9
$\begingroup$

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?

$\endgroup$
4
  • $\begingroup$ Proposed duplicate: (7688) Also related: (17497) $\endgroup$
    – Mr.Wizard
    Commented Aug 28, 2016 at 7:00
  • $\begingroup$ amrods, thanks for the Accept. Do you feel that your question is a duplicate of 7688 linked above? If so we should mark it as such. $\endgroup$
    – Mr.Wizard
    Commented Aug 29, 2016 at 3:50
  • $\begingroup$ I don't think it's a duplicate. $\endgroup$
    – amrods
    Commented Aug 29, 2016 at 4:25
  • $\begingroup$ You could use Quiet also. $\endgroup$ Commented Aug 31, 2016 at 0:24

5 Answers 5

7
$\begingroup$

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:

$\endgroup$
6
$\begingroup$

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"} *)
$\endgroup$
1
  • $\begingroup$ Change the levelspec as needed. (If your list is level 1, then {1} would be enough) $\endgroup$ Commented Aug 28, 2016 at 3:58
6
$\begingroup$

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"}}} *)
$\endgroup$
1
$\begingroup$
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.

$\endgroup$
1
$\begingroup$

An alternative is to use StringCases and Except:

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

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

(*{"y", "z"}*)
$\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.