Consider the following:
data={"AB","CD","AF"};
Now I would like to delete all String from data
which starts with "A".
Result: {"CD"}
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*"] &)]
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$
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$
Commented
Apr 19, 2012 at 17:20
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
data
. But, it works, so +1.
$\endgroup$
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$
Commented
Apr 19, 2012 at 16:01
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$
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.
data // DeleteCases[_?strQ1]
data // DeleteCases[_?strQ2]
data // DeleteCases[_?strQ3]
data // DeleteCases[_?strQ4]
data // DeleteCases[_?strQ5]
data // Cases[Except[_?strQ1]]
data // Cases[Except[_?strQ2]]
data // Cases[Except[_?strQ3]]
data // Cases[Except[_?strQ4]]
data // Cases[Except[_?strQ5]]
data // Pick[#,strQ1/@#,False]&
data // Pick[#,strQ2@#,False]&
data // Pick[#,strQ3@#,False]&
data // Pick[#,strQ4@#,False]&
data // Pick[#,strQ5@#,False]&
data // Select[strQ1Neg]
data // Select[strQ2Neg]
data // Select[strQ3Neg]
data // Select[strQ4Neg]
data // Select[strQ4Neg2]
data // Select[strQ5Neg]
data // Select[strQ5Neg2]
{"CD"}
I have decided to benchmark those answers above to find which one is the fastest. This is the procedure I have followed.
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}}
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]
}
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.
{$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.
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$
Commented
Aug 25, 2016 at 1:55
Another method using Select
but with what I find to be a more obvious notation.
Select[d, StringMatchQ[#, Except["A"] ~~ __] &]
Here's yet another solution that has not been mentioned using Select
and StringFreeQ
Select[{"AB", "CD", "AF"}, StringFreeQ[#, "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"}
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.
data // Pick[#, # // StringStartsQ[#, "A"] &, False] &
I was not aware of StringStartsQ
until I saw your answer
$\endgroup$
DeleteDuplicates[data,First@Characters[#]=="A"]
but abviously this is not working. $\endgroup$