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}
6
  • 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, 2017 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, 2017 at 20:15
  • So you just want \Splitlist to blanket split on both , and ;, correct?
    – Werner
    Jan 10, 2017 at 20:16
  • Yes, but I couldn't find what the syntax might be for specifying that.
    – lotomat
    Jan 10, 2017 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, 2017 at 21:09

4 Answers 4

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.

6
  • how can I substitute spaces? for e.g. \foo{a b c}
    – lotomat
    Jan 11, 2017 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, 2017 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, 2017 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, 2017 at 22:08
  • @lotomat It's even less clear, I'm afraid.
    – egreg
    Jan 11, 2017 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}
3
  • What will become of \foo{a,b;c}? ;-)
    – egreg
    Jan 10, 2017 at 21:06
  • @egreg you get the expected outcome (not only expected but correct, no?) Jan 10, 2017 at 21:09
  • @egreg answer updated Jan 10, 2017 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .