Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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})
share|improve this question
2  
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

5 Answers 5

up vote 11 down vote accepted

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

share|improve this answer
    
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

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}
share|improve this answer

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}
share|improve this answer

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}
share|improve this answer

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}%
  }}%
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.