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]


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?

  • $\begingroup$ 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? $\endgroup$
    – Sascha
    Dec 26 '16 at 21:02
  • $\begingroup$ This particular one is for visualization purposes. Helping students with archaic words. $\endgroup$
    – bcan
    Dec 27 '16 at 2:44

I prefer avoiding TraditionalForm strings, e.g.,

(* "\!\(\*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`"};
    StringReplace[txt, Thread[Rule[words, indices]]],
    Sequence @@ Thread[Tooltip[words, tooltips]]
(* 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[
    Sow[ToLowerCase[word] /. Thread[Rule[words, tooltips]], word],

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

Internal`InheritedBlock[{i=1, toIndex},
        StringForm[#1, Sequence@@#2]&,
            StringReplace[txt, x:Alternatives@@words :> toIndex[x], IgnoreCase->True],
            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"]] *)
  • $\begingroup$ One might also choose to use StringTemplate[] instead of StringForm[]; this route requires a preliminary ToString[Tooltip[(* stuff *)], StandardForm], tho. $\endgroup$ 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]


  • 1
    $\begingroup$ I noticed that this only works when specifying a form with ToString, such as TraditionalForm or StandardForm. $\endgroup$
    – Sascha
    Dec 26 '16 at 23:12
  • $\begingroup$ @Sascha Yes, that's because the default is OutputForm which is no good here. $\endgroup$
    – 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];
% // Head

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.