9

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}
\begin{equation}\label{eq:first}\end{equation}
\begin{equation}\label{eq:second}\end{equation}
\begin{equation}\label{eq:third}\end{equation}
\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})
jlperla
  • 570

5 Answers5

11

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

enter image description here

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}

enter image description here

egreg
  • 1,121,712
  • 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 06 '14 at 22:50
  • @jlperla I think this is worthy a new question, where you can spell out precisely the requirements. – egreg Mar 06 '14 at 23:03
  • specification added. – jlperla Mar 06 '14 at 23:23
  • @jlperla Added change. – egreg Mar 06 '14 at 23:51
7

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

enter image description here

\documentclass{article}

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

\makeatother

\begin{document}

\begin{equation}\label{eq:first}\end{equation}
\begin{equation}\label{eq:second}\end{equation}
\begin{equation}\label{eq:third}\end{equation}


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

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

C \erefs{eq:first}

\end{document}
David Carlisle
  • 757,742
6

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}%
  }}%
Nicola Talbot
  • 41,153
5

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

enter image description here

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}

Peter Grill
  • 223,288
0

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.

xintforlast

\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}
\begin{equation}E=mc^2\label{eq:first}\end{equation}
\begin{equation}E=h\nu\label{eq:second}\end{equation}
\begin{equation}\zeta(s)=0\label{eq:third}\end{equation}

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

\erefs{eq:first}

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

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