5
$\begingroup$

I can use a string template like so:

st = StringTemplate["a is `a` and b is `b`"];
st @ <|"a" -> 1, "b" -> 2|>

which gives

"a is 1 and b is 2"

But I would like the parameter b to be optional, so that if I call

st @ <|"a" -> 1|>

I get simply

"a is 1"

Something like this is possible with <* *>: For the case where b must be numeric, I can use

StringTemplate["a is `a`<* If[NumericQ[#b],\" and b is \",\"\"]*>`b`"]

but is there a way to tell whether a template parameter is present or absent, rather than numeric or otherwise?

$\endgroup$
5
  • $\begingroup$ You can use KeyMemberQ[#,"b"] to check whether "b" is present in the parameters. But I'm not sure StringTemplate is really the best option for something like this... $\endgroup$
    – Lukas Lang
    May 16, 2019 at 13:54
  • $\begingroup$ @LukasLang What else would you use? $\endgroup$ May 16, 2019 at 14:11
  • $\begingroup$ @StephenPowell see the edit in my answer. $\endgroup$
    – Kagaratsch
    May 16, 2019 at 14:22
  • $\begingroup$ How general it should be? Are there more optional key:value pairs? Do you want the template to care about "a" "b" and potential others? $\endgroup$
    – Kuba
    May 17, 2019 at 6:20
  • $\begingroup$ @Kuba Where I needed it, there was a single optional parameter, but the general case might be useful for others. $\endgroup$ May 17, 2019 at 7:29

5 Answers 5

6
$\begingroup$

You can use MissingQ instead of NumericQ:

template = StringTemplate["a is `a`<* If[!MissingQ[#b],\" and b is \",\"\"]*>`b`"];

Then:

template @ <|"a"->1|>
template @ <|"a"->1, "b"->2|>

"a is 1"

"a is 1 and b is 2"

$\endgroup$
2
  • $\begingroup$ Thanks. I think this is the most direct solution to the question as posed. I hadn't appreciated what is actually happening when the key "b" is missing from the association: #b gives Missing["SlotAbsent","b"] and then TextString applied to that gives an empty string, which is spliced in. $\endgroup$ May 17, 2019 at 7:40
  • $\begingroup$ It looks like the TemplateExpression isn't being syntax-colored, both in Mathematica and in Markdown/Prettify. Just wondering aloud. $\endgroup$
    – Roman
    May 17, 2019 at 9:25
4
$\begingroup$

Sorry for not using templates:

StringRiffle[ KeyValueMap[List]@<|"a" -> 1, "b" -> 2|>, " and ", " is "]
$\endgroup$
4
  • $\begingroup$ You inverted the levels of is and and. $\endgroup$
    – Roman
    May 17, 2019 at 9:30
  • $\begingroup$ @Roman O_o thanks :) $\endgroup$
    – Kuba
    May 17, 2019 at 9:33
  • $\begingroup$ I learn a lot by trying out your solutions. It's like studying a foreign language and learning from the native speakers. Thanks! $\endgroup$
    – Roman
    May 17, 2019 at 9:47
  • $\begingroup$ @Roman Thanks for kind words, I learned a lot this way as well. $\endgroup$
    – Kuba
    May 17, 2019 at 9:53
2
$\begingroup$
st[sub_] := If[KeyMemberQ[sub, "b"], StringTemplate["a is `a` and b is `b`"], StringTemplate["a is `a`"]]@sub;
st[<|"a" -> 1|>]
st[<|"a" -> 1, "b" -> 2|>]

"a is 1"

"a is 1 and b is 2"

ALTERNATIVE:

Or you could make it more easily scalable for more different cases by defining:

strings = {{"a", "a is `a`"}, {"b", " and b is `b`"}};
compose[strings_, sub_] := Block[{st = ""},
  Do[If[KeyMemberQ[sub, element[[1]]], st = st <> element[[2]]];, {element, strings}];
  StringTemplate[st]@sub
]

the strings variable contains snippets to be added when first element is present. The function compose checks all cases and constructs the template

compose[strings, <|"a" -> 1|>]

"a is 1"

and

compose[strings, <|"a" -> 1, "b" -> 2|>]

"a is 1 and b is 2"

just add more entries to strings as you wish.

$\endgroup$
1
$\begingroup$

An old-fashioned solution?

st2[l_] := 
 l[[1, 1]] <> " is " <> ToString[l[[1, 2]]] <> 
  Table[" and " <> l[[n, 1]] <> " is " <> ToString[l[[n, 2]]], {n, 2, Length[l]}]

st2[{{"a", 1}}]

"a is 1"

st2[{{"a", 1}, {"b", 2}}]

"a is 1 and b is 2"

st2[{{"a", 1}, {"b", 2}, {"w", 5}, {"x", 10}}]

"a is 1 and b is 2 and w is 5 and x is 10"

$\endgroup$
0
$\begingroup$

Here's an implementation that uses a single StringTemplate and no (* *):

With[{st = 
   StringTemplate["a is `a` and b is `b`",
      InsertionFunction -> Replace[y : Except[_Missing] :> TextString[y]],
      CombinerFunction -> (StringJoin @@ BlockMap[Replace[{_, _Missing} -> Nothing], #, 2] &)]},
   {st[<|"a" -> 1, "b" -> 2|>], st[<|"a" -> 1|>]}
]

(* {"a is 1 and b is 2", "a is 1"} *)

The InsertionFunction makes sure that Missing["SlotAbsent",_] is not converted to "", and the CombinerFunction drops anything that is _Missing, and the preceding string.

$\endgroup$

Your Answer

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

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