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
    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
    Aug 29, 2016 at 3:50
  • $\begingroup$ I don't think it's a duplicate. $\endgroup$
    – amrods
    Aug 29, 2016 at 4:25
  • $\begingroup$ You could use Quiet also. $\endgroup$ 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$ 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$

This site is temporarily in read-only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .