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.

When writing LaTeX code, I often successively make changes like adding or removing items. I'd like to have TeX count and update the number of items in the list automatically, as in

There are \numItemsNextList ways how a parameter can be given:
\begin{itemize}
\item By a constant expression.
\item By user interaction.
\item From another database.
\end{itemize}

(what happens next is of course that I realize there is yet another way to give a parameter and add it to the list)

I could roll out my own preprocessor to just count the number of \item-commands in the following \begin{...} ... \end{...} block. But is there a TeX solution as well?

share|improve this question
    
Somewhat related: How to make enumerate items align at left margin? –  Werner Sep 29 '12 at 15:54

4 Answers 4

up vote 14 down vote accepted

Here's a way: we modify the itemize environment with etoolbox tools; each \item command will step a counter; every environment steps another. A couple of LaTeX runs can be necessary for the numbers to stabilize.

\documentclass{article}
\usepackage{etoolbox,refcount}

\newcounter{countitems}
\newcounter{nextitemizecount}
\newcommand{\setupcountitems}{%
  \stepcounter{nextitemizecount}%
  \setcounter{countitems}{0}%
  \preto\item{\stepcounter{countitems}}%
}
\makeatletter
\newcommand{\computecountitems}{%
  \edef\@currentlabel{\number\c@countitems}%
  \label{countitems@\number\numexpr\value{nextitemizecount}-1\relax}%
}
\newcommand{\nextitemizecount}{%
  \getrefnumber{countitems@\number\c@nextitemizecount}%
}
\makeatother
\AtBeginEnvironment{itemize}{\setupcountitems}
\AtEndEnvironment{itemize}{\computecountitems}

\begin{document}

There are \nextitemizecount{} ways how a parameter can be given:
\begin{itemize}
\item By a constant expression.
\item By user interaction.
\item From another database.
\end{itemize}

There are \nextitemizecount{} ways how a parameter can be given:
\begin{itemize}
\item By a constant expression.
\item By user interaction.
\item From another database.
\item Foo.
\end{itemize}

The next doesn't count.

\begin{itemize}
\item A
\item B
\end{itemize}

There are \nextitemizecount{} ways how a parameter can be given:
\begin{itemize}
\item By a constant expression.
\item By user interaction.
\end{itemize}

\end{document}

enter image description here

share|improve this answer
    
Chose this one because it precisely fits my request. Thanks a lot for the more complete solution below, too! –  rainmaker Oct 1 '12 at 20:25

I am not sure how to do it with itemize. But with enumerate environment you could add a label to last item.

\documentclass{article}

\begin{document}
There are \ref{lst:num} ways how a parameter can be given:
\begin{enumerate}
    \item By a constant expression.
    \item By user interaction.
    \item From another database. 
    \label{lst:num}
\end{enumerate}

\end{document}
share|improve this answer
    
+1 for the very simplicity! –  yo' Sep 29 '12 at 15:10
    
+1 This is the only solution so far that provides a correct count with nested lists. –  Peter Grill Oct 1 '12 at 2:29
    
@PeterGrill now this has changed; another solution also works with nested lists. –  Gonzalo Medina Oct 1 '12 at 3:46
    
A drawback of this solution (and others proposed here) is that it is conceptually strange to refer to the last item in a list in order to count the number of items (the count has not much to do with the last item in particular). As an illustration of that problem, hyperref will create a link from the printed count to the last item. Any idea how to prevent the count to generate such a link? (Without disabling every links of course.) –  Olivier Cailloux Nov 4 at 17:28

With the help of the enumitem package you can define a list based on enumerate, but behaving as itemize as far as labels are concerned. In this way one is allowed to place \labels, in particular to the last item. The following example illustrates such definition (for a list environment with four-level nesting) and also shows that the approach works with nesting:

\documentclass{article}
\usepackage{enumitem}

\newlist{citemize}{enumerate}{4}
\setlist[citemize,1]{label=$\bullet$,ref=\arabic*}
\setlist[citemize,2]{label=--,ref=\arabic*}
\setlist[citemize,3]{label=$\ast$,ref=\arabic*}
\setlist[citemize,4]{label=$\cdot$,ref=\arabic*}

\begin{document}

There are~\ref{lst:num} ways how a parameter can be given, and user interaction has~\ref{lst:subnum} possibilities:
\begin{citemize}
  \item By a constant expression.
  \item By user interaction.
  \begin{citemize}
    \item First subitem.
    \item Second subitem.
    \label{lst:subnum}
  \end{citemize}
  \item From another database. 
  \label{lst:num}
\end{citemize}

\end{document}

enter image description here

share|improve this answer
    
This is what I had in mind but did not know how to implement. –  mythealias Oct 1 '12 at 3:49
    
Yep, this also works with nested lists, and provides a count of an itemized list. –  Peter Grill Oct 1 '12 at 3:51

The following code provides a redefined itemize environment.

Update

An answer that suits your request a little better …
\items are labelable if you use the option "Lableable Items" (\item[<opt>]\label{<key>}).

Code

\documentclass{article}
\usepackage{xspace}         % used in the \numItemsNextList command: http://ctan.org/pkg/xparse
\newcounter{myItemCounter}          % counts items in the itemize list
\newcounter{myInternalLabelCounter} % increments so that every itemize environment gets it own label
\let\olditem\item               % save original commands: \item
\let\olditemize\itemize         %                         \begin{itemize}
\let\oldenditemize\enditemize   %                         \end{itemize}

\newif\ifcountitems             % \numItemsNextList set countitems true, otherwise no counting occurs

\newcommand*\numItemsNextList{%
    \countitemstrue%                                         initial: set countitems=true
    \setcounter{myItemCounter}{0}%                           initial: myItemCounter=0
    \stepcounter{myInternalLabelCounter}%                    initial: myInternalLabelCounter++
    \ref{myInternalLabelKey\arabic{myInternalLabelCounter}}% needs 2nd run
    \xspace%                                                 provides space without the need to use \numItemsNextList{}
}
\renewenvironment{itemize}{%
  \olditemize%
}{%
  \ifcountitems% only set label if we count at all
%    \addtocounter{myItemCounter}{-1}% Without "Labelable Items": comment out if you have labelable items
%    \refstepcounter{myItemCounter}%   Without "Labelable Items": comment out if you have labelable items
    \label{myInternalLabelKey\arabic{myInternalLabelCounter}}%
  \fi%
  \oldenditemize%
  \global\countitemsfalse% sets countitems=false (end of itemize environment)
}
\renewcommand*\item{%
  \ifcountitems%
%  \stepcounter{myItemCounter}%     Without "Labelable Items": If you use this comment out next line!
  \refstepcounter{myItemCounter} % With "Labelable Items":    If you use this comment out the line above!
  \fi%
  \olditem%
}

\begin{document}
There are \numItemsNextList ways how a parameter can be given:
\begin{itemize}
\item By a constant expression.
\item By user interaction.
\item From another database.
\end{itemize}

Without I:
\begin{itemize}
\item Not countable.
\end{itemize}

There are \numItemsNextList ways how a parameter can be given (\#\ref{lstitm:important} is a good one.):
\begin{itemize}
\item By a constant expression.
\item By user interaction.
\item\label{lstitm:important} From another database.
\item (Not really.)
\end{itemize}

Without II:
\begin{itemize}
\item Not countable.
\item Not countable.
\end{itemize}
\end{document}

Output

enter image description here

Old Solution

This solution can refer to any itemize environment with the optional argument.

Code

\documentclass{article}
\newcounter{myItemCounter}
\let\olditem\item
\let\olditemize\itemize
\let\oldenditemize\enditemize

\makeatletter
\newcommand*\@empty@string{}
\newcommand*\count@label{}
\renewcommand*\item{\refstepcounter{myItemCounter}\olditem}

\renewenvironment{itemize}[1][]{%
    \setcounter{myItemCounter}{0}%
    \renewcommand*\count@label{#1}%
    \olditemize%
    }{%
    \ifx\count@label\@empty@string\else%
        \label{\count@label}%
    \fi%
    \oldenditemize%
}
\newcommand*{\labelitem}[1]{%
    \refstepcounter{myItemCounter}\label{#1}%
    \olditem%
}
\makeatother

\begin{document}
This list contains \ref{lst:ct} item(s), the \ref{lst:important}. is important:
\begin{itemize}
    \item[a] First item.
    \labelitem{lst:important}[x] Next item.
    \labelitem{lst:ct} Last item?
\end{itemize}

This list contains \ref{lst:ct2} item(s):
\begin{itemize}[lst:ct2]
    \item First item.
    \item[i] Last item?
\end{itemize}
\end{document}

Output

compiled code

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.