6

How can I tell \SplitList from xparse to split the argument at any one of several delimiters, e.g. either a comma, a semi-colon, etc. The following example works with a list of items separated by commas, but I would like it to work also when the delimiter is a semi-colon.

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\foo{>{\SplitList{ , }}m}{{\ProcessList{#1}{\textsf}}}
\begin{document}
 Test \foo{a,b,c} \foo{a;b;c}
\end{document}
  • You could make it work using \foo{a,b,c} and \foo[;]{a;b;c}. In other words, you specify an optional argument that could be used as the \SplitList parameter. – Werner Jan 10 '17 at 20:02
  • Thanks for your suggestion but I don't want to do that because ultimately I will use that function inside another one which will take its own options. – lotomat Jan 10 '17 at 20:15
  • So you just want \Splitlist to blanket split on both , and ;, correct? – Werner Jan 10 '17 at 20:16
  • Yes, but I couldn't find what the syntax might be for specifying that. – lotomat Jan 10 '17 at 20:27
  • 1
    What would be the fate of \foo{a,b;c}? Should it be equivalent to \foo{a,b,c} and \foo{a;b;c} or behave differently? – egreg Jan 10 '17 at 21:09
6

You can do it, but it's better to tell explicitly what delimiter you're going to using for the particular list.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand\foo{O{,}mO{\textsf}}
 {
  \seq_set_split:Nnn \l_lotomat_input_seq { #1 } { #2 }
  \seq_map_function:NN \l_lotomat_input_seq #3
 }
\seq_new:N \l_lotomat_input_seq
\ExplSyntaxOff

\begin{document}

Test \foo{a,b,c} \foo[;]{a;b;c} \foo[:]{a:b:c}[\textbf]

\end{document}

We have to use lower level functions, in order to be able to change the delimiter. I also added the possibility of giving a different one argument function as trailing optional argument.

enter image description here

Here is a different implementation where the delimiter can be either a comma or a semicolon (you can add other substitutions):

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand\foo{mO{\textsf}}
 {
  \tl_set:Nn \l_lotomat_input_tl { #1 }
  \tl_replace_all:Nnn \l_lotomat_input_tl { ; } { , }
  \seq_set_split:NnV \l_lotomat_input_seq { , } \l_lotomat_input_tl
  \seq_map_function:NN \l_lotomat_input_seq #2
 }
\tl_new:N \l_lotomat_input_tl
\seq_new:N \l_lotomat_input_seq
\ExplSyntaxOff

\begin{document}

Test \foo{a,b,c} \foo{a;b;c} \foo{a,b;c}[\textbf]

\end{document}

The output is the same as before.

  • how can I substitute spaces? for e.g. \foo{a b c} – lotomat Jan 11 '17 at 19:41
  • also, how can I recombine the items with a different delimiter between them after the application of the function on each item? – lotomat Jan 11 '17 at 21:02
  • @lotomat You're asking contradictory questions. Stick to a single delimiter (optionally changeable as shown in the first example). – egreg Jan 11 '17 at 21:45
  • here's what I want to do: split the list on any delimiter, apply a command to each item, print the sequence of modified items with a different delimiter between each item. – lotomat Jan 11 '17 at 22:08
  • @lotomat It's even less clear, I'm afraid. – egreg Jan 11 '17 at 22:09
5

using \fbox rather than \textsf to make it clearer what has split, you can cascade two list processors (although it's probably bad form to do so).

enter image description here

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\foo{>{\SplitList{,}}m}{\ProcessList{#1}\foob}
\NewDocumentCommand\foob{>{\SplitList{;}}m}{{\ProcessList{#1}\fbox}}
\begin{document}
 Test \foo{a,b,c} \foo{a;b;c} \foo{egreg,asked;this}
\end{document}
  • What will become of \foo{a,b;c}? ;-) – egreg Jan 10 '17 at 21:06
  • @egreg you get the expected outcome (not only expected but correct, no?) – David Carlisle Jan 10 '17 at 21:09
  • @egreg answer updated – David Carlisle Jan 10 '17 at 21:11
4

You can perform a search-and-replace to make sure all your split tokens are the same:

enter image description here

\documentclass{article}

\usepackage{regexpatch}

\NewDocumentCommand\foo{ m }{%
  \def\fooarg{#1}%
  \regexpatchcmd*{\fooarg}{;}{,}{}{}% Replace all ; with ,
  \expandafter\footwo\expandafter{\fooarg}}

\NewDocumentCommand\footwo{>{\SplitList{ , }}m}{{\ProcessList{#1}{\textsf}}}

\begin{document}

Test \foo{a,b,c} \foo{a;b;c}

\end{document}
4

Here is a listofitems approach, where the possible separators are defined by \setsepchar argument, separated by || "or". Here, , and ; are given.

\documentclass{article}
\usepackage{listofitems}
\newcommand\foo[1]{\setsepchar{,||;}\readlist\foolist{#1}%
  \foreachitem\x\in\foolist{\fbox{\x}}}
\begin{document}
 Test \foo{a,b,c} \foo{a;b;c}

The list length is \foolistlen.  Item 2 is \foolist[2].
\end{document}

The length of the list is available as \foolistlen and the items are accessible as \foolist[1], \foolist[2], etc.

enter image description here

It should furthermore be noted that the original unexpanded tokens can be retrieved by expanding \foolist[] exactly twice:

\documentclass{article}
\usepackage{listofitems}
\usepackage[T1]{fontenc}
\newcommand\foo[1]{\setsepchar{,||;}\readlist\foolist{#1}%
  \foreachitem\x\in\foolist{\fbox{\x}}}
\begin{document}
 Test \foo{a,b,c} \foo{a;\textit{b};c}

The list length is \foolistlen.  Item 2 is \foolist[2], 

stored as \detokenize\expandafter\expandafter\expandafter{\foolist[2]}.
\end{document}

enter image description here

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.