6
$\begingroup$

If I have a list of strings Like:

text = Select[
  StringSplit[
   StringReplace[
    StringTake[ExampleData[{"Text", "AliceInWonderland"}], {25, 997}], 
    Alternatives @@ Characters["<>?;'|:\"()-_!&"] -> " "], "." | ","],
   StringLength[#] > 2 &]

{"Alice was beginning to get very tired of sitting by her sister on the bank"," and of having nothing to do"," Once or twice she had peeped into the book her sister was reading"," but it had no pictures or conversations in it"," and what is the use of a book"," thought Alice"," without pictures or conversations So she was considering in her own mind as well as she could"," for the day made her feel very sleepy and stupid "," whether the pleasure of making a daisy chain would be worth the trouble of getting up and picking the daisies"," when suddenly a White Rabbit with pink eyes ran close by her"," There was nothing so very remarkable in that"," nor did Alice think it so very much out of the way to hear the Rabbit say to itself"," Oh dear Oh dear I shall be too late But when the Rabbit actually took a watch out of its waistcoat pocket and looked at it and then hurried on"," Alice started to her feet"," for it flashed across her mind that she had never before seen a rabbit with "}

how do I find the positions of the list elements that contain the following words/character strings:

words = {"her", "she", "alice", "bbit"};

Following on from this question, I tried using Position like so:

Position[text, 
 s_ /; StringMatchQ[s, __ ~~ Alternatives @@ words ~~ __, 
    IgnoreCase -> True] &]

{}

but this doesn't produce the list of positions. The follwing seems to suggest that I have the right string-matching criteria:

StringMatchQ[text, __ ~~ Alternatives @@ words ~~ __, 
 IgnoreCase -> True]

{True,False,True,False,False,False,True,True,True,True,True,True,True,True,True}

What am I missing?

$\endgroup$

2 Answers 2

8
$\begingroup$

StringPosition[] supports the useful form:

StringPosition[ {string1, string2 ...}, {patt1, patt2, ...} ]

So:

Position[StringPosition[text, words, IgnoreCase -> True], Except@{}, 1, Heads -> False]

{{1}, {3}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}}

Edit

This approach seems much faster than using conditional pattern matching:

text = Join @@ Array[Select[StringSplit[StringReplace[StringTake[
               ExampleData[{"Text", "AliceInWonderland"}], {25, 997}], 
              Alternatives @@ Characters["<>?;'|:\"()-_!&"] -> " "], 
              "." | ","], StringLength[#] > 2 &] &, 1000];
words = {"her", "she", "alice", "bbit"}; 

First@Timing@Position[
    text,
    s_ /; StringMatchQ[s, ___ ~~ Alternatives @@ words ~~ ___, IgnoreCase -> True],
    Heads -> False]

First@Timing@Position[
    StringPosition[text, words, IgnoreCase -> True],
    Except@{},
    1, Heads -> False]

First@Timing@Position[
    StringMatchQ[text, ___ ~~ Alternatives @@ words ~~ ___, IgnoreCase -> True],
    True]

0.168
0.042
0.044

$\endgroup$
2
  • 1
    $\begingroup$ Using the listability of StringMatchQ it gets comparable speeds: Position[StringMatchQ[text, ___ ~~ Alternatives @@ words ~~ ___, IgnoreCase -> True], True] or Pick[Range[Length[text]], StringMatchQ[text, ___ ~~ Alternatives @@ words ~~ ___, IgnoreCase -> True]] $\endgroup$
    – ssch
    Sep 22, 2013 at 20:07
  • $\begingroup$ @ssch Could you edit my answer comparing the three methods? $\endgroup$ Sep 22, 2013 at 20:35
5
$\begingroup$

I misunderstood the question first...

I think you want to get rid of the & in Position:

Position[text, 
         s_ /; StringMatchQ[s, ___ ~~ Alternatives @@ words ~~ ___, 
         IgnoreCase -> True], Heads -> False]

(* {{1}, {3}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}} *)

As it is pattern /; expression and not pattern/;function

Edit old answer

You can use StringPosition:

matches = StringPosition[text, Alternatives @@ words]
(* {{{53, 55}}, {}, {{16, 18}, {45, 47}}, {}, {}, {}, {{42, 44}, {65, 
   67}, {90, 92}}, {{19, 21}}, {{6, 8}}, {{26, 29}, {59, 61}}, {{3, 
   5}}, {{67, 70}}, {{58, 61}}, {{19, 21}}, {{12, 14}, {24, 26}, {38, 
   40}, {68, 71}}} *)

MapThread[
 StringReplacePart[#1, ToUpperCase[StringTake[#1, #2]], #2] &,
 {text, matches}]
(* {
  "Alice was beginning to get very tired of sitting by HER sister on the bank",
  " and of having nothing to do",
  " Once or twice SHE had peeped into the book HER sister was reading",
  " but it had no pictures or conversations in it",
  "  and what is the use of a book", 
  "  thought Alice", 
  "  without pictures or conversations   So SHE was considering in HER own mind  as well as SHE could",
  " for the day made HER feel very sleepy and stupid ",
  " whetHER the pleasure of making a daisy chain would be worth the trouble of getting up and picking the daisies",
  " when suddenly a White RaBBIT with pink eyes ran close by HER",
  " THERe was nothing so very remarkable in that",
  " nor did Alice think it so very much out of the way to hear the RaBBIT say to itself",
  "  Oh dear  Oh dear  I shall be too late   But when the RaBBIT actually took a watch out of its waistcoat pocket and looked at it and then hurried on",
  " Alice started to HER feet", " for it flaSHEd across HER mind that SHE had never before seen a raBBIT with "} *)
$\endgroup$
7
  • $\begingroup$ You suggestion tells me where these words occur within each list element, but not which elements contain any of these words. Also, "alice" wasn't found (simply add IgnoreCase->True). $\endgroup$
    – geordie
    Sep 22, 2013 at 14:44
  • $\begingroup$ @geordie how about now? :) $\endgroup$
    – ssch
    Sep 22, 2013 at 14:46
  • $\begingroup$ Yep, that works nicely. Thanks! btw. Why is it necessary to set Heads -> False? Is this the same as adding 'levelspec' to Position(e.g. Position[list, blah, {1}])? $\endgroup$
    – geordie
    Sep 22, 2013 at 14:55
  • 1
    $\begingroup$ It's so it doesn't try to StringMatchQ[List,...] changing the pattern to s_String would have the same effect. It is distinct from levelspec. $\endgroup$
    – ssch
    Sep 22, 2013 at 14:58
  • 1
    $\begingroup$ @belisarius thanks! $\endgroup$
    – ssch
    Sep 22, 2013 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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