# Match patterns on list of Strings

Consider the following:

data={"AB","CD","AF"};


Now I would like to delete all String from data which starts with "A".

Result: {"CD"}

• May be somthing like DeleteDuplicates[data,First@Characters[#]=="A"] but abviously this is not working. – John Apr 19 '12 at 15:07
• It is well known that Wolfram Language favors immutability, therefore deletion functionality can be meant in many ways, e.g. selecting strings that match a pattern, cases of string patterns, picking string patterns. But in all cases, including the DeleteCases, a copy of the data list with those patterns that do not match these cases is returned. Therefore deletion here is not meant in a mutable way like it often happens with other programming languages, e.g. remove an element from a Python list. This may be too obvious for mature users of the language, but for a newcomer it is frustrating. – Athanassios Aug 24 '16 at 13:00

I am not sure how to do this using DeleteCases, but you can still use the Select function:

Select[data, StringTake[#, 1] != "A" &]


which has the desired result.

Edit Actually, you can also use DeleteCases like this:

DeleteCases[data, _?(StringTake[#, 1] == "A" &)]


Here is another one:

DeleteCases[data, _?(StringMatchQ[#, "A*"] &)]

• The OP should take careful note of the parentheses around StringMatchQ[#, "A*"]& as the parser for PatternTest (?) is aggressive. Without it, the parser comes up with Function[PatternTest[Blank[], StringMatchQ][Slot[1], "A*"]] as opposed to the correct form PatternTest[Blank[], Function[StringMatchQ[Slot[1], "A*"]]]. – rcollyer Apr 19 '12 at 15:21
• I don't want to post it as a new answer, as there are already quite a handful, and it is very similar to the one above but uses Condition instead of PatternTest: DeleteCases[data, x_ /; StringMatchQ[x, "A*"]]. And of course there are a million ways to write the same pattern differently. – István Zachar Apr 19 '12 at 17:20
• Thank you @IstvánZachar, that made me search on the differences between PatternTest and Condition. I do not think these are covered from WL Documentation. But I found this post enlightening ! – Athanassios Aug 24 '16 at 9:47
 Pick[data, StringMatchQ[#, "A*"] & /@ data, False]
(* => {"CD"} *)


EDIT: As noted in YvesKlett's comment, since StringMatchQ threads over its first argument, we can also use

 Pick[data, StringMatchQ[data, "A*"], False]


or

 Pick[#, StringMatchQ[#, "A*"], False]&@data

• The disadvantage I can see is the double pass over data. But, it works, so +1. – rcollyer Apr 19 '12 at 15:24
• Pick[data, StringMatchQ[data, "A*"], False] should work as well, since it accepts a list of strings as first argument... saves on wear and tear on your keyboard ;-) – Yves Klett Apr 19 '12 at 16:01
• @YvesKlett, right! Thank you. – kglr Apr 19 '12 at 16:36
• @rcollyer, thanks for the vote. For large lists, Pick "usually" more than compensates for the double-pass overhead associated with creation of the selector array, provided, of course, the selector array is carefully constructed. – kglr Apr 19 '12 at 17:00

Another method using Select but with what I find to be a more obvious notation.

Select[d, StringMatchQ[#, Except["A"] ~~ __] &]


## Synopsis

It is not really an answer but a synopsis on answers of this post. I thought it will be useful for newcomers of WL patterns like me, so I thought to share it with you ;-)

Answers can be categorized in terms of the function and the pattern test. This is the list of patterns I have taken. I have also included negated forms that are used with Select.

strQ1   =StringTake[#,1]=="A"&;
strQ1Neg=StringTake[#,1] !="A" &;

strQ2   =StringStartsQ["A"];
strQ2Neg=Not@*StringStartsQ["A"];

strQ3   =StringMatchQ[#,"A*"]&;
strQ3Neg=Not@*strQ3;

strQ4    =StringMatchQ[#,"A"~~__]&;
strQ4Neg =StringMatchQ[#,Except["A"]~~__]&;
strQ4Neg2=StringFreeQ[#,"A"~~__]&;

strQ5    =StringMatchQ[#,"A"~~WordCharacter]&;
strQ5Neg =StringMatchQ[#,Except["A"]~~WordCharacter]&;
strQ5Neg2=StringFreeQ[#,"A"~~WordCharacter]&;


These can be used with the following WL functions, I use operational forms, wherever possible, and postfix notation to highlight the transformation.

### DeleteCases

data // DeleteCases[_?strQ1]
data // DeleteCases[_?strQ2]
data // DeleteCases[_?strQ3]
data // DeleteCases[_?strQ4]
data // DeleteCases[_?strQ5]


### Cases

data // Cases[Except[_?strQ1]]
data // Cases[Except[_?strQ2]]
data // Cases[Except[_?strQ3]]
data // Cases[Except[_?strQ4]]
data // Cases[Except[_?strQ5]]


### Pick

data // Pick[#,strQ1/@#,False]&
data // Pick[#,strQ2@#,False]&
data // Pick[#,strQ3@#,False]&
data // Pick[#,strQ4@#,False]&
data // Pick[#,strQ5@#,False]&


### Select

data // Select[strQ1Neg]
data // Select[strQ2Neg]
data // Select[strQ3Neg]
data // Select[strQ4Neg]
data // Select[strQ4Neg2]
data // Select[strQ5Neg]
data // Select[strQ5Neg2]


{"CD"}

## Benchmarking

I have decided to benchmark those answers above to find which one is the fastest. This is the procedure I have followed.

### Timing

Each computation is measured with Timing that is repeated 100 times. Then I am taking the Mean of the results, e.g.

q1DeleteCases = Table[data // DeleteCases[_?strQ1] // Timing // First, {100}] // Mean


All values are converted to milliseconds, my $TimeUnit is 1/100. These are all the results per category. {timingCases, timingDeleteCases, timingPick, timingSelect}  {{133.151,677.71,94.879,314.711,310.773},{115.781,674.823,84.6535,296.377,287.192},{94.8139,40.2126,8.64794,18.9432,18.7907},{89.8032,716.253,105.113,263.977,149.96,262.98,149.862}} ### Barchart Plotting patternTestsQ1toQ5={"q1:StringTake", "q2:StringStartsQ", "q3:StringMatchQ[#,\"A*\"]", "q4:StringMatchQ[#,\"A\"~~__]", "q5:StringMatchQ[#,\"A\"~~WordCharacter"}; patternTestsSelect = {"q1Neg", "q2Neg", "q3Neg", "q4Neg", "q4Neg2", "q5Neg", "q5Neg2"}; { BarChart[timingDeleteCases, ChartElementFunction->"GlassRectangle",ChartStyle->"Pastel", ChartLegends->patternTestsQ1toQ5,AxesLabel->{"DeleteCases","ms"},TargetUnits->"ms",ChartLabels->Placed[timingDeleteCases,Top],ImageSize->Large], BarChart[timingCases, ChartElementFunction->"GlassRectangle",ChartStyle->"Pastel", ChartLegends->patternTestsQ1toQ5,AxesLabel->{"Cases","ms"},TargetUnits->"ms",ChartLabels->Placed[timingCases,Top],ImageSize->Large], BarChart[timingPick, ChartElementFunction->"GlassRectangle",ChartStyle->"Pastel", ChartLegends->patternTestsQ1toQ5,AxesLabel->{"Pick","ms"},TargetUnits->"ms",ChartLabels->Placed[timingPick,Top],ImageSize->Large], BarChart[timingSelect, ChartElementFunction->"GlassRectangle",ChartStyle->"Pastel", ChartLegends->patternTestsSelect,AxesLabel->{"Select","ms"},TargetUnits->"ms",ChartLabels->Placed[timingSelect,Top],ImageSize->Large] }  ### BarChart Comparison Comparison of the first three BarCharts, columns are grouped by pattern test with a function chart legend and timing data labels at the top of each bar. One can notice that timing for DeleteCases is slightly faster than Cases and there is a significant improvement on the speed of calculations for pattern matching that is based on the Pick function. ### System Information {$OperatingSystem,$ProcessorCount,$ProcessorType, MemoryInUse[],\$Version}


{Unix,2,x86-64,257092208,10.3.1 for Linux x86 (64-bit) (December 8, 2015)}

All Pick answers are much faster compared to other solutions, but the one with this PatternTest q3:StringMatchQ[#,"A*"] is significantly faster. I am leaving justification to the experienced user of WL.

• (+1) Just in case I point you to this answer for short discussion on the difference between Timing and AbsoluteTiming. String-matching functions internally use the PCRE library, and I can't to say for sure whether the CPU time spent in this library is included in Timing or not. – Alexey Popkov Aug 25 '16 at 1:55

Here's yet another solution that has not been mentioned using Select and StringFreeQ

Select[{"AB", "CD", "AF"}, StringFreeQ[#, "A" ~~ ___] &]
(* {"CD"} *)

Cases[data, Except[_?(StringMatchQ[#, "A*"] &)]]

{"CD"}


A possibly dangerous version that works for your example:

data = {"AB", "CD", "AF"};
Flatten[StringCases[data, Except["A"] ~~ __]]

{"CD"}


uh, and adding variety to the Pick faction:

Pick[data, Thread[StringTake[data, 1] != "A"]]

{"CD"}

• +1 for posting the dangerous version. I came up with that one as well, and thought, "I don't like the look of this" – tkott Apr 19 '12 at 16:27
• Sometimes you gotta take a risk ;-) – Yves Klett Apr 19 '12 at 16:47
• Can you elucidate why the first one is dangerous? – rcollyer Apr 19 '12 at 17:02
• I didn't test but you might get tangled up with other string configurations more easily? – Yves Klett Apr 19 '12 at 17:24

Here's a solution which may add flexibility if one is working with more complicated strings or required patterns

Select[ data, StringMatchQ[#, "A" ~~ WordCharacter] & ]


If, for example, one would want to select from data2 those strings which start with a numeric digit and which have "C" as a second digit:

data2 = {"AB1", "C2D", "3AF", "A41", "5CD", "FG6"};
Select[ data2, StringMatchQ[#, DigitCharacter ~~ "C" ~~ WordCharacter] & ]


This is a perfect fit for the newish (10.1) StringStartsQ function and "curried" operators:

Select[data, Not @* StringStartsQ["A"]]
(* {"CD"} *)


EDIT. Also, here's a silly approach using Pick's optional "pattern" argument:

Pick[data, Characters[data], {Except["A"], __String}]
(* {"CD"} *)


The redundant-looking String qualifier is there because of the rather weird way that Pick uses pattern arguments.

• Slight modification: data // Pick[#, # // StringStartsQ[#, "A"] &, False] & I was not aware of StringStartsQ until I saw your answer – user1066 Aug 24 '16 at 19:25