1

I want to combine the automatic punctuation in comma separated list from this answer with the list processing from etoolbox. However, I do not get any output. What is going wrong here?

\documentclass{article}

\usepackage{xparse,environ,xspace,etoolbox}

\ExplSyntaxOn \NewEnviron{commalist}[1][\space]{\spence_comma_list:V \BODY #1} \seq_new:N \l_spence_items_seq \cs_new_protected:Npn \spence_comma_list:n #1 { \seq_set_split:Nnn \l_spence_items_seq { \item } { #1 } \seq_pop_left:NN \l_spence_items_seq \l_tmpa_tl % we have an empty element at the beginning \seq_use:Nnnn \l_spence_items_seq { ~ and ~ } { , ~ } { , ~ and ~ } } \cs_generate_variant:Nn \spence_comma_list:n { V } \ExplSyntaxOff

\begin{document} The new list works: \begin{commalist} \item a \item b \item c \end{commalist}

List processing from etoolbox works: \forcsvlist{;}{a,b,c}

The combination does not work: \begin{commalist} \forcsvlist{\item}{a,b,c} \end{commalist} \end{document}

Jan Hajer
  • 864
  • Other than for academic interest, why would you even need the commalist environment, if you are providing input in the form of a comma separated list. Would an answer that takes a comma separated list and turns it into a, b and c be equally acceptable? – Steven B. Segletes May 05 '21 at 13:11
  • I have a bibtex field containing a comma separated list. I am reading it using \forcsvfield from biblatex which is modeled after \forcsvlist from etoolbox. I want to include an and before the last item, but are also considering using a totally different separator, maybe a semicolon. If there is a better way to do it, I will gladly accept it. – Jan Hajer May 05 '21 at 13:18

2 Answers2

1

I am not sure that this syntax will support what the OP is trying to achieve, but the issue is, as is often the case, EXPANSION.

\documentclass{article}
\usepackage{xparse,environ,xspace,etoolbox}

\ExplSyntaxOn \NewEnviron{commalist}[1][\space]{\spence_comma_list:V \BODY #1} \seq_new:N \l_spence_items_seq \cs_new_protected:Npn \spence_comma_list:n #1 { \seq_set_split:Nnn \l_spence_items_seq { \item } { #1 } \seq_pop_left:NN \l_spence_items_seq \l_tmpa_tl % we have an empty element at the beginning \seq_use:Nnnn \l_spence_items_seq { ~ and ~ } { , ~ } { , ~ and ~ } } \cs_generate_variant:Nn \spence_comma_list:n { V } \ExplSyntaxOff

\begin{document} \def\tmp{\begin{commalist}} \edef\z{\forcsvlist{\noexpand\item}{a,b,c}} \expandafter\tmp\z \end{commalist} \end{document}

enter image description here

However, if one is not forced to use the commalist environment, other ways are easier:

\documentclass{article}
\usepackage{listofitems}
\newcommand\oxcomma[1]{%
  \readlist*\mylist{#1}%
  \foreachitem\z\in\mylist[]{%
    \ifnum\zcnt=1\else
      \ifnum\listlen\mylist[]<3\else,\fi\ \fi
    \ifnum\zcnt=\listlen\mylist[]\relax and \fi
    \z
  }
}
\newcommand\nooxcomma[1]{%
  \readlist*\mylist{#1}%
  \foreachitem\z\in\mylist[]{%
    \ifnum\zcnt=1\else
      \ifnum\zcnt=\listlen\mylist[]\relax\ and \else, \fi
    \fi
    \z
  }
}
\begin{document}
\noindent OPTION A:\\ % OXFORD COMMA
\oxcomma{A, B}\\
\oxcomma{A, B, C}

\medskip\noindent OPTION B:\ \nooxcomma{A, B}\ \nooxcomma{A, B, C} \end{document}

enter image description here

  • Nice! I did try expansion, but this was too involved for me. The solution using commalist lost the ability to execute code directly before the begin of the item e.g. \item\bf. The Oxford comma solution fails for two entries, as there I would like to have no comma at all, only the and. I have to look into the listofitems package, I never used it before. – Jan Hajer May 05 '21 at 13:39
  • @JanHajer Please see my revision to fix your cited deficiency of the 2nd approach. – Steven B. Segletes May 05 '21 at 14:19
  • @JanHajer As to the commalist approach, you can either do it \edef\z{\forcsvlist{\noexpand\item\noexpand\bfseries}{a,b,c}} or else \edef\z{\forcsvlist{\noexpand\item}{\noexpand\bfseries a,b,c}} – Steven B. Segletes May 05 '21 at 14:22
  • By the way, @JanHajer, listofitems can provide powerful multi-level (nested) parsing. Definitely check it out (https://ctan.org/pkg/listofitems). – Steven B. Segletes May 05 '21 at 14:26
1

Here is one way using the b argument type of \NewDocumentEnvironment (thus not needing the environ package) and the \unexpanded primitive of e-TeX in order not to expand the contents too early (the harmless a,b,c in the example).

\documentclass{article}
% xspace not needed
% xparse not needed if the LaTeX kernel is from 2020-10-01 or later
\usepackage{xparse,etoolbox}

\ExplSyntaxOn \NewDocumentEnvironment { commalist } { +b } { \spence_comma_list:n {#1} } { }

\seq_new:N \l__spence_items_seq

% From egreg: <https://tex.stackexchange.com/a/62620/73317> \cs_new_protected:Npn \spence_comma_list:n #1 { \seq_set_split:Nnn \l__spence_items_seq { \item } { #1 } % We have an empty element at the beginning: \seq_pop_left:NN \l__spence_items_seq \l_tmpa_tl \seq_use:Nnnn \l__spence_items_seq { ~ and ~ } { , ~ } { , ~ and ~ } } \ExplSyntaxOff

\begin{document}

The combination works: \begingroup \renewcommand{\do}[1]{\noexpand\item \unexpanded{#1}}% \edef\zzz{% \endgroup \noexpand\begin{commalist} \docsvlist{a,b,c}% }\zzz \end{commalist}.

\end{document}

enter image description here

frougon
  • 24,283
  • 1
  • 32
  • 55