\SplitList on several delimiters

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
• 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

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.

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

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).

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\foo{>{\SplitList{,}}m}{\ProcessList{#1}\foob}
\NewDocumentCommand\foob{>{\SplitList{;}}m}{{\ProcessList{#1}\fbox}}
\begin{document}
\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

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

\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}


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}
\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.

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}