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$
  • $\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 at 13:54
  • $\begingroup$ @LukasLang What else would you use? $\endgroup$ – Stephen Powell May 16 at 14:11
  • $\begingroup$ @StephenPowell see the edit in my answer. $\endgroup$ – Kagaratsch May 16 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 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$ – Stephen Powell May 17 at 7:29
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$
  • $\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$ – Stephen Powell May 17 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 at 9:25
4
$\begingroup$

Sorry for not using templates:

StringRiffle[ KeyValueMap[List]@<|"a" -> 1, "b" -> 2|>, " and ", " is "]
$\endgroup$
  • $\begingroup$ You inverted the levels of is and and. $\endgroup$ – Roman May 17 at 9:30
  • $\begingroup$ @Roman O_o thanks :) $\endgroup$ – Kuba May 17 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 at 9:47
  • $\begingroup$ @Roman Thanks for kind words, I learned a lot this way as well. $\endgroup$ – Kuba May 17 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, privacy policy and cookie policy

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