30

In my document, I have made an enumerate with several items I will need to reference to. They are numbered with a simple (1), (2)... , but I need a (2') as well. To reference nicely, I was using enumitem, this fails upon changing one label:

\documentclass{article}
\usepackage{enumitem}
\begin{document}

\begin{enumerate}[label=\textnormal{(\arabic*)}]
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \item[(2')] manually different label\label{itm:2b}.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

This produces, as a final line, the following:

Referencing to (1)(2)(2)(3).

How could I fix this?

4 Answers4

27

You could define it like this:

\documentclass{article}
\usepackage{enumitem}
\makeatletter
\newcommand{\mylabel}[2]{#2\def\@currentlabel{#2}\label{#1}}
\makeatother
\begin{document}
\begin{enumerate}[label=\textnormal{(\arabic*)}]
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \item[\mylabel{itm:2b}{(2')}] manually different label.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

enter image description here

4

This is an old post, but all of the existing answers define some new commands and then require additional massaging to get the (2') label to work. A better approach is to "fix" the labeling once and forall so that the referencing works as expected when the enumerate environment is used in the "normal" way:

\begin{enumerate}
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \item[(2')] manually different label\label{itm:2b}.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

As Ambika Vanchinathano says, to do this we need to redefine the value of \@currentlabel whenever \item is given an optional argument. The following lines of code define a new item command, \myItem, that does exactly this (I use the xparse package because it gives a cleaner way of dealing with optional arguments):

\let\realItem\item  % save the original item command
\NewDocumentCommand\myItem{ o }{%
   \IfNoValueTF{#1}%
      {\realItem}% add an item
      {\realItem[#1]\def\@currentlabel{#1}}% add an item and update label
}

That is, \myItem uses the original \item command to set the label and it updates \@currentlabel when it is given an optional argument. In practice, we need to wrap these commands inside \makeatletter...\makeatother but the code above is essentially all we need.

We can tell the enumerate environment to use \myItem instead of \item using \setlist:

\setlist[enumerate]{before=\let\item\myItem}% make \item=\myItem

This approach will work without change if you use the hyperref package.

Putting this all together, here is full code. I have put all of the formatting from the MWE into the \setlist command:

\documentclass{article}
\usepackage{xparse}
\let\realItem\item % save a copy of the original item
\makeatletter
\NewDocumentCommand\myItem{ o }{%
   \IfNoValueTF{#1}%
      {\realItem}% add an item
      {\realItem[#1]\def\@currentlabel{#1}}% add an item and update label
}
\makeatother

\usepackage{enumitem}
\setlist[enumerate]{ before=\let\item\myItem, % use \myItem in enumerate label=\textnormal{(\arabic*)}, % format the label widest=(2') % set the widest label }

\begin{document}

\begin{enumerate} \item text1\label{itm:1}. \item text2\label{itm:2}. \item[(2')] manually different label\label{itm:2b}. \item text3\label{itm:3}. \end{enumerate} Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

This results in the expected output:

enter image description here

In practice, rather than redefining the enumerate environment to work like this you might want to create a new enumerate-like environment using the \newlist command from the enumitem package.

Actually, I am a little surprised that the enumerate environments do not redefine \@currentlabel like this when \item has an optional argument...

3

If one is using hyperref, the easiest way to print the prime and have a link pointing to the right item is to modify slightly the solution by Ambika Vanchinathan by adding a dummy counter (that is, adding \newcounter{dummy} in the preamble and \refstepcounter{dummy} inside the definition of \mylabel).

However, personally I would rather redefine \item (instead of \label) through:

\makeatletter
\newcommand\myitem[1][]{\item[#1]\refstepcounter{dummy}\def\@currentlabel{#1}}
\makeatother

With this definition, manual-labeled items should be introduced as

\myitem[(2')]\label{itm:2b}

That is, changes are minimal with respect to the usual way of introducing things, so that the new definition is easier to remember. I would also define a math-mode prime through

\newcommand*{\mprime}{\ensuremath{'}}

Complete MWE:

\documentclass{article}
\newcounter{dummy}
\usepackage{hyperref}
\usepackage{enumitem}
\makeatletter
\newcommand\myitem[1][]{\item[#1]\refstepcounter{dummy}\def\@currentlabel{#1}}
\makeatother
\newcommand*{\mprime}{\ensuremath{'}}

\begin{document}
\begin{enumerate}[label=\textnormal{(\arabic*)}]
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \myitem[(2\mprime)]\label{itm:2b} manually different label.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}
summer
  • 1,474
1

You can do it dynamically like this, and not have to input the "2" yourself; you just pass a suffix, such as ', as an argument. This solution works with and without hyperref, cleveref, and enumitem, basically relying on the new \labelsuffix command:

\documentclass{article}
\usepackage{xpatch}

\usepackage{hyperref}
\usepackage{cleveref}
\usepackage{enumitem}

% latex.ltx
% \def\item{ 
% - sets \@noitemargtrue if no argument
% - calls \@item
% \def\@item[#1]{
% - checks \if@noitemarg
% - calls \refstepcounter only if true
% ==> \item[] does not call \refstepcounter
% \def\refstepcounter#1{\stepcounter{#1}
% - defines \@currentlabel
% \def\label#1{\@bsphack
% - uses \@currentlabel
% ==> need to set \@currentlabel manually before calling label

% cleveref.sty
% \item looks unchanged (no mention)
% \refstepcounter is changed!
% \def\refstepcounter{%
% - calls \refstepcounter@optarg or \refstepcounter@noarg
% - basically the same flow, both call the old \refstepcounter
% - same problem: call to \refstepcounter was skipped; 
% we have set \@currentlabel manually, and need to do similarly for \cref@currentlabel

\makeatletter
\newcommand{\labelsuffix}[2]{%
    % this is required for hyperref:
    \addtocounter{\@listctr}{-1}%
    \refstepcounter{\@listctr}%

    % fix for \ref:
    \edef\@currentlabel{\@currentlabel#2}%

    % fix for \cref (if you need to, load hyperref *before* cleveref):
    % TODO: this ignores prefixes and counter aliases!
    % TODO: to fix, copy/patch additional parts from \refstepcounter@noarg (from cleveref.sty)
    \global\def\cref@currentlabel{[\@listctr][\arabic{\@listctr}][]\@currentlabel}%

    % fix without enumitem
    \expandafter\edef\csname the\@listctr\endcsname{\csname the\@listctr\endcsname#2}%

    % fix with enumitem: label\@listctr does not contain the\@listctr, but c@\@listctr directly.
    % so we patch; silently fails when enumitem is not loaded.
    % https://tex.stackexchange.com/questions/340620/
    \expandafter\def\expandafter\label@listctr\expandafter{\csname label\@listctr\endcsname}%
    \expandafter\def\expandafter\c@@listctr\expandafter{\csname c@\@listctr\endcsname}%

    \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter%
    \xpatchcmd\expandafter\expandafter\expandafter%
    \label@listctr\expandafter\expandafter\expandafter%
        {\expandafter\expandafter\expandafter%
            {\expandafter%
                \c@@listctr\expandafter%
            }\expandafter%
        }\expandafter%
        {\expandafter%
            {\c@@listctr}#2%
        }{}{err}%

    \csname label\@listctr\endcsname%
    \label{#1}%
}
\makeatother

\begin{document}
    \begin{enumerate}
    \item\label{item:A}

    \item\label{item:Aa}
    \begin{enumerate}

        \item\label{item:B}

        \item[\labelsuffix{item:C}{'}]

        \item[\labelsuffix{item:D}{*}]

        \item\label{item:E}
    \end{enumerate}

    \item[\labelsuffix{item:F}{'}]

    \item\label{item:G}

    \item[\labelsuffix{item:H}{*}]

    \end{enumerate}

    items \ref{item:A}, \ref{item:Aa}, \ref{item:B}, \ref{item:C}, \ref{item:D}, \ref{item:E}, \ref{item:F}, \ref{item:G} and \ref{item:H}

    \cref{item:A,,item:Aa,,item:B,,item:C,,item:D,,item:E,,item:F,,item:G,,item:H}

    (cref does not sort items in different levels correctly -- I have filed that as a bug with the developer.)

\end{document}
bers
  • 5,404