# StringReplace adds “~~” to words with Tooltip

I'm adding tooltip to certain words in a text. I guess since words with tooltips are not considered as strings, it gives the ~~ operators before and after the word in the result. Needless to say, I don't need them in the result. I have a huge text and thousands of words so looking for a fast-running operation. Thanks

txt = "Good, better, best. Never let it rest. 'Til your good is
better and your better is best.";
words = {"good", "better", "best"};
tooltips = {"good2", "better2", "best2"};
sw = {words, tooltips}\[Transpose];
wordsWithTooltips = Tooltip[#[[1]], #[[2]]] & /@ sw;
tls = {words, wordsWithTooltips}\[Transpose];
StringReplace[txt, #[[1]] -> #[[2]] & /@ tls]


Result:

Good, ~~better~~, ~~best~~. Never let it rest. 'Til your ~~good~~ is ~~better~~ and your ~~better~~ is ~~best~~.

Side question; note that first word (Good) didn't get a tooltip since it starts with a capital letter. How can I set IgnoreCase to True in this type of setting?

• Out of curiosity: do you want to annotate certain words just for visualization purposes or do you intend to tag the string for some data mining / machine learning task? – Sascha Dec 26 '16 at 21:02
• This particular one is for visualization purposes. Helping students with archaic words. – bcan Dec 27 '16 at 2:44

I prefer avoiding TraditionalForm strings, e.g.,

ToString[Tooltip[x/y,stuff],TraditionalForm]//FullForm
(* "\!$$\*FormBox[TagBox[TooltipBox[FractionBox[\"x\", \"y\"], \"stuff\"], Function[Annotation[Slot[1], stuff, \"Tooltip\"]]], TraditionalForm]$$" *)


Instead, I would use StringForm. The simplest version would be:

indices = {"1", "2", "3"};
StringForm[
]
(* StringForm["Good, 2, 3. Never let it rest. 'Til your 1 is 2 and your
2 is 3.", Tooltip["good", "good2"], Tooltip["better", "better2"],
Tooltip["best", "best2"]] *)


However, you wanted to use IgnoreCase. The Tooltip will have to be different for different cases, and a priori, you don't know how many versions with different cases there will be. So, the indices will have to be generated dynamically as the string is processed. To do this, I introduce a helper function:

toIndex[word_] := toIndex[word] = CompoundExpression[
""<>ToString[i++]<>""
]


Then, I use this helper function to convert words into their indexed form:

InternalInheritedBlock[{i=1, toIndex},
Apply[
StringForm[#1, Sequence@@#2]&,
Reap[
StringReplace[txt, x:Alternatives@@words :> toIndex[x], IgnoreCase->True],
_String,
Tooltip[#1, #2[[1]]]&
]
]
]
(* StringForm["1, 2, 3. Never let it rest. 'Til your 4 is 2 and your
2 is 3.", Tooltip["Good", "good2"], Tooltip["better", "better2"],
Tooltip["best", "best2"], Tooltip["good", "good2"]] *)

• One might also choose to use StringTemplate[] instead of StringForm[]; this route requires a preliminary ToString[Tooltip[(* stuff *)], StandardForm], tho. – J. M. will be back soon Dec 27 '16 at 3:05

This is because the elements of wordsWithTooltips aren't strings. If you convert to strings, there's no issue.

StringReplace[txt, #1 -> ToString[#2, TraditionalForm] & @@@ tls]


• I noticed that this only works when specifying a form with ToString, such as TraditionalForm or StandardForm. – Sascha Dec 26 '16 at 23:12
• @Sascha Yes, that's because the default is OutputForm which is no good here. – Mr.Wizard Dec 31 '16 at 7:04

This question is closely related to:

As Chip Hurst already explained your Tooltip expressions need to be converted to strings if they are to be appended as strings. You could also display the expression as a Row instead:

Row@*List @@ StringReplace[txt, Rule @@@ tls]


If that expression needs to be converted to a string it can be done en masse:

ToString[%, StandardForm];

String
`