17
$\begingroup$

Consider the following:

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

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

Result: {"CD"}

$\endgroup$
  • $\begingroup$ May be somthing like DeleteDuplicates[data,First@Characters[#]=="A"] but abviously this is not working. $\endgroup$ – John Apr 19 '12 at 15:07
  • $\begingroup$ 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. $\endgroup$ – Athanassios Aug 24 '16 at 13:00

10 Answers 10

13
$\begingroup$

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" &)]
|improve this answer|||||
$\endgroup$
15
$\begingroup$

Here is another one:

DeleteCases[data, _?(StringMatchQ[#, "A*"] &)]
|improve this answer|||||
$\endgroup$
  • 4
    $\begingroup$ 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*"]]]. $\endgroup$ – rcollyer Apr 19 '12 at 15:21
  • 2
    $\begingroup$ 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. $\endgroup$ – István Zachar Apr 19 '12 at 17:20
  • $\begingroup$ 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 ! $\endgroup$ – Athanassios Aug 24 '16 at 9:47
7
$\begingroup$
 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
|improve this answer|||||
$\endgroup$
  • $\begingroup$ The disadvantage I can see is the double pass over data. But, it works, so +1. $\endgroup$ – rcollyer Apr 19 '12 at 15:24
  • 1
    $\begingroup$ 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 ;-) $\endgroup$ – Yves Klett Apr 19 '12 at 16:01
  • $\begingroup$ @YvesKlett, right! Thank you. $\endgroup$ – kglr Apr 19 '12 at 16:36
  • 1
    $\begingroup$ @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. $\endgroup$ – kglr Apr 19 '12 at 17:00
5
$\begingroup$

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

Select[d, StringMatchQ[#, Except["A"] ~~ __] &]
|improve this answer|||||
$\endgroup$
5
$\begingroup$

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]

Output

{"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]
}

enter image description here

enter image description here

enter image description here

enter image description here

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.

enter image description here

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)}

Fastest Answer

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.

|improve this answer|||||
$\endgroup$
  • $\begingroup$ (+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. $\endgroup$ – Alexey Popkov Aug 25 '16 at 1:55
4
$\begingroup$

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

Select[{"AB", "CD", "AF"}, StringFreeQ[#, "A" ~~ ___] &]
(* {"CD"} *)
|improve this answer|||||
$\endgroup$
3
$\begingroup$
Cases[data, Except[_?(StringMatchQ[#, "A*"] &)]]
{"CD"}
|improve this answer|||||
$\endgroup$
3
$\begingroup$

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

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] & ]
|improve this answer|||||
$\endgroup$
1
$\begingroup$

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.

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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