# Special case for last element with \foreach

I want to create a function that generates a list of equation references from a comma delimited input list. e.g.

\erefs{eq:first, eq:second, eq:third}
\erefs{eq:first}


yields, the following:

(\ref{eq:first}), (\ref{eq:second}), and (\ref{eq:third})
(\ref{eq:first})


I am able to take care of the 1 item list correctly , but am having trouble generating the 'and' for the last element. Any ideas on how to add the 'and' to this code?

\documentclass{article}
\usepackage{tikz, amsmath, hyperref}

\def\erefs#1{%
\gdef\firstelement{1}
\foreach \e [count=\ni] in {#1}{%
\ifnum\firstelement=0 , \fi %
(\ref{\e})%
\gdef\firstelement{0}%
}
}

\begin{document}
$$\label{eq:first}$$
$$\label{eq:second}$$
$$\label{eq:third}$$
\erefs{eq:first, eq:second, eq:third}
\erefs{eq:first}
\end{document}


I need to use hyperref and amsmath packages concurrently

ADDITION: I frequently use \package{xr} and \externaldocument{...} to refer to a technical appendix, and like to put TA. in front of those references. It would be nice if an optional parameter could be passed for a prefix

I want to create a function that generates a list of equation references from a comma delimited input list. e.g.

\erefs{eq:first, eq:second, eq:third}
\erefs{eq:first}
\erefs[TA]{eq:first, eq:second, eq:third}
\erefs[TA]{eq:first}


yields, the following:

(\ref{eq:first}), (\ref{eq:second}), and (\ref{eq:third})
(\ref{eq:first})
(TA.\ref{eq:first}), (TA.\ref{eq:second}), and (TA.\ref{eq:third})
(TA.\ref{eq:first})

-
Have you seen the cleveref package? It does this sort of thing out of the box. –  Paul Gessler Mar 6 at 21:22
I haven't, thank you. Very useful. Still would love to learn how to do this for other cases though. –  jlperla Mar 6 at 21:30
Alas, cleveref is incompatible with using amsmath and hyperref at the same time, which I have. But looks like a great package. –  jlperla Mar 6 at 21:37
possible duplicate of pgffor: Special treatment for last item in \foreach-list –  Peter Grill Mar 6 at 21:42
Perhaps forcsvlist from etoolbox package might help? –  Christian Hupfer Mar 6 at 21:45

Here's a way without cleveref, but it needs a recent version of expl3:

\documentclass{article}
\usepackage{xparse,amsmath}

\ExplSyntaxOn
\NewDocumentCommand{\erefs}{sm}
{
\IfBooleanTF{#1}
{ \jlperla_erefs:Nn \ref { #2 } }
{ \jlperla_erefs:Nn \eqref { #2 } }
}
\seq_new:N \l_jlperla_input_seq
\seq_new:N \l_jlperla_output_seq
\cs_new_protected:Npn \jlperla_erefs:Nn #1 #2
{
\seq_set_split:Nnn \l_jlperla_input_seq { , } { #2 }
\seq_clear:N \l_jlperla_output_seq
\seq_map_inline:Nn \l_jlperla_input_seq
{
\seq_put_right:Nn \l_jlperla_output_seq { #1 { ##1 } }
}
\seq_use:Nnnn \l_jlperla_output_seq
{ ~ and ~ }   % between two
{ , ~ }       % between more than two
{ , ~ and ~ } % between last two
}
\ExplSyntaxOff

\begin{document}
Some text before
\begin{align}
0+0&=0\label{eq:first}\\
0+1&=1\label{eq:second}\\
1+1&=2\label{eq:third}
\end{align}

One: \erefs{eq:first}

Two: \erefs{eq:second, eq:third}

Three: \erefs{eq:first,eq:second,eq:third}

One: \erefs*{eq:first}

Two: \erefs*{eq:second, eq:third}

Three: \erefs*{eq:first,eq:second,eq:third}

\end{document}


The *-version uses \ref, while the normal version uses \eqref (which is better for equations).

The “input” sequence is set to the labels, the cycle adds \ref or \eqref around them. Then \seq_use:Nnnn does the right thing in all cases.

Here's the modification for allowing a prefix. I hook into \eqref, defining a similar command in order the special formatting is applied.

\documentclass{article}
\usepackage{xparse,amsmath}

\makeatletter
\newcommand{\peqref}[2]{\textup{\tagform@{#1\ref{#2}}}}
\newcommand{\pref}[2]{#1\ref{#2}}
\makeatother

\ExplSyntaxOn
\NewDocumentCommand{\erefs}{ s o m }
{
\IfBooleanTF{#1}
{
\IfNoValueTF{#2}
{ \jlperla_erefs:Nnn \pref { } { #3 } }
{ \jlperla_erefs:Nnn \pref { #2. } { #3 } }
}
{
\IfNoValueTF{#2}
{ \jlperla_erefs:Nnn \peqref { } { #3 } }
{ \jlperla_erefs:Nnn \peqref { #2. } { #3 } }
}
}
\seq_new:N \l_jlperla_input_seq
\seq_new:N \l_jlperla_output_seq
\cs_new_protected:Npn \jlperla_erefs:Nnn #1 #2 #3
{
\seq_set_split:Nnn \l_jlperla_input_seq { , } { #3 }
\seq_clear:N \l_jlperla_output_seq
\seq_map_inline:Nn \l_jlperla_input_seq
{
\seq_put_right:Nn \l_jlperla_output_seq { #1 { #2 } { ##1 } }
}
\seq_use:Nnnn \l_jlperla_output_seq
{ ~ and ~ }   % between two
{ , ~ }       % between more than two
{ , ~ and ~ } % between last two
}
\ExplSyntaxOff

\begin{document}
Some text before
\begin{align}
0+0&=0\label{eq:first}\\
0+1&=1\label{eq:second}\\
1+1&=2\label{eq:third}
\end{align}

No prefix

One: \erefs{eq:first}

Two: \erefs{eq:second, eq:third}

Three: \erefs{eq:first,eq:second,eq:third}

One: \erefs*{eq:first}

Two: \erefs*{eq:second, eq:third}

Three: \erefs*{eq:first,eq:second,eq:third}

\bigskip

With prefix

One: \erefs[TA]{eq:first}

Two: \erefs[TA]{eq:second, eq:third}

Three: \erefs[TA]{eq:first,eq:second,eq:third}

One: \erefs*[TA]{eq:first}

Two: \erefs*[TA]{eq:second, eq:third}

Three: \erefs*[TA]{eq:first,eq:second,eq:third}

\end{document}


-
Beautiful. Thank you. Sorry to bother you with one last requirement, which I had trouble modifying this to include as I don't entirely understand it. What if I want to pass in an optional parameter which I use to prepend the reference if it exists (I refer to a technical appendix in my paper frequently, and use a TA. in front of the references). i.e. \erefs[TA]{TA:eq:myeq} to (TA.\eqref{TA:eq:myeq})? –  jlperla Mar 6 at 22:50
@jlperla I think this is worthy a new question, where you can spell out precisely the requirements. –  egreg Mar 6 at 23:03
specification added. –  jlperla Mar 6 at 23:23
@jlperla Added change. –  egreg Mar 6 at 23:51

Here's a general use method that uses etoolbox:

\documentclass{article}

\usepackage{etoolbox}

% set up defaults so we don't get an error
% when we try to redefine these commands
\newcommand*{\elementsep}{}%
\newcommand*{\lastelement}{}%
\newcommand*{\prelastelement}{}%

% define the handler macro:
\newcommand*{\dodisplayelement}[1]{%
\elementsep
\lastelement
\renewcommand{\lastelement}{%
\renewcommand{\elementsep}{, }%
\renewcommand{\prelastelement}{ and }%
#1%
}}%

% define the new command to process a list of elements:
\newcommand*{\displayelements}[1]{%
% initialise:
\renewcommand*{\elementsep}{}%
\renewcommand*{\lastelement}{}%
\renewcommand*{\prelastelement}{}%
% Iterate through list
\forcsvlist{\dodisplayelement}{#1}%
% Finish off:
\prelastelement \lastelement
}

\begin{document}

\displayelements{first,second,third,fourth,fifth}

\end{document}


Each item is displayed by \lastelement so if the item represented a label you could use:

% define the handler macro:
\newcommand*{\dodisplayelement}[1]{%
\elementsep
\lastelement
\renewcommand{\lastelement}{%
\renewcommand{\elementsep}{, }%
\renewcommand{\prelastelement}{ and }%
\ref{#1}%
}}%

-

Here is an adaptation from pgffor: Special treatment for last item in \foreach-list to provide special case handling of the last element:

## Code:

\documentclass{article}
\usepackage{tikz,amsmath}

\newcounter{TotalNumberOfListMembers}%
\newcommand{\SetTotalNumberOfListMembers}[1]{%
\setcounter{TotalNumberOfListMembers}{0}%
\foreach \member in {#1} {%
\stepcounter{TotalNumberOfListMembers}%
}%
}%

\newcounter{CurrentListMemberCount}%
\newcommand*{\erefs}[1]{%
\SetTotalNumberOfListMembers{#1}%
\setcounter{CurrentListMemberCount}{0}%
\foreach \n  in {#1}{%
\stepcounter{CurrentListMemberCount}%
\ifnum\arabic{CurrentListMemberCount}=1
(\ref{\n})%
\else%
\ifnum\arabic{CurrentListMemberCount}=\arabic{TotalNumberOfListMembers}
\ifnum\arabic{CurrentListMemberCount}=2
\ and
\else
, and
\fi
(\ref{\n})
\else
, (\ref{\n})%
\fi
\fi
}%
}%

\begin{document}
Some text before
\begin{align}
0+0&=0\label{eq:first}\\
0+1&=1\label{eq:second}\\
1+1&=2\label{eq:third}
\end{align}

One: \erefs{eq:first}

Two: \erefs{eq:second, eq:third}

Three: \erefs{eq:first,eq:second,eq:third}

\end{document}

-

You don't really need any additional package, you can just use the loops already in LaTeX:

\documentclass{article}

\makeatletter
\def\erefs#1{%
\count@\z@
\edef\xtmp{\ifcase\count@\or\or\ and\ \else, and\ \fi}%
\@for\tmp:=#1\do{%
\edef\tmp{%
\noexpand\ref{\expandafter\zap@space\tmp\@gobble{} \@empty}}%
\tmp
\ifnum\count@>\@ne, %
\else
\ifnum\count@=\@ne\xtmp
\fi\fi}}

\makeatother

\begin{document}

$$\label{eq:first}$$
$$\label{eq:second}$$
$$\label{eq:third}$$

A \erefs{eq:first, eq:second, eq:third}

B \erefs{eq:first, eq:second}

C \erefs{eq:first}

\end{document}

-

With \xintFor. The optional parameter may be anything, it does not have to be TA.

If an expandable way had been asked for, this could be done with some other utilities from xinttools.

\documentclass{article}
\usepackage{amsmath, hyperref}
\usepackage{xinttools}

\makeatletter
% with optional parameter.
\def\erefs {\@ifnextchar[\@erefsTA\@erefs }

\def\@erefs #1{%
\xintFor ##1 in {#1}\do
{\xintifForFirst{}{, \xintifForLast{and }{}}(\ref{##1})}%
}

\def\@erefsTA [#1]#2{%
\xintFor ##1 in {#2}\do
{\xintifForFirst{}{, \xintifForLast{and }{}}(#1, \ref{##1})}%
}

\makeatother

\begin{document}\thispagestyle{empty}
$$E=mc^2\label{eq:first}$$
$$E=h\nu\label{eq:second}$$
$$\zeta(s)=0\label{eq:third}$$

\erefs{eq:first, eq:second, eq:third}

\erefs{eq:first}

\erefs[TA]{eq:first, eq:second, eq:third}

\erefs[TA]{eq:first}
\end{document}

-