# Compress a list of page references

I'm looking for a pure (La)TeX solution (alas, no LuaTeX) for compressing a list of page references (given with \pageref) like cite.sty does with citations; that is "Compress lists of three or more consecutive numbers to one number range." The numbers are already sorted and can have up to four digits. Basically what MakeIndex does but in TeX.

Input would be like \pageref{foo}, \pageref{bar}, \pageref{foobar}, \pageref{quax} (where the delimiter can be choosen as needed) and the output should be something like "1, 2--4".

-
Have you try the package cleveref ? – projetmbc Oct 18 '11 at 17:18
@projetmbc: Not yet. But it seems to deal only with \ref, not \pageref. – Martin Schröder Oct 18 '11 at 17:39
@projetmbc: The latest version does; see below. – Martin Schröder Oct 18 '11 at 20:12

The latest version (V0.18) of cleveref includes \cpageref and \cpagerefrange which can be used to refer to page ranges as well. It is not yet on CTAN, so for the test below I obtained it from Toby Cubitt's site:

\documentclass{book}
\usepackage{lipsum}

\begin{document}

\chapter{One}
\section{One-One}\label{One-One}
\lipsum[1-4]\label{One-Two}

\chapter{Two}
\section{Two-One}\label{Two-One}
\section{Two-Two}\label{Two-Two}
\section{Two-Three}\label{Two-Three}

\chapter{Three}
\section{Three-One}\label{Three-One}
\lipsum[1-9]

\newpage
Section One-One is on Page~\pageref{One-One}\par
Section One-Two is on Page~\pageref{One-Two}\par
Section Two-One is on Page~\pageref{Two-One}\par
Section Two-Two is on Page~\pageref{Two-Two}\par
Section Two-Three is on Page~\pageref{Two-Three}\par
Section Three-One is on Page~\pageref{Three-One}\par

\bigskip
Using the \textbf{cleveref package (V0.18)}:\par
\bigskip
See \cpageref{One-One,One-Two,Three-One}\par
See \cpageref{One-One,One-Two,Two-One,Two-Two,Three-One}\par
See \cpagerefrange{One-One}{Three-One}\par
\end{document}

-
That's exactly what I was looking for. Thanks! – Martin Schröder Oct 18 '11 at 20:11

This has not turned out as pretty as I initially hoped, but it seems to work. It takes a list of integers and it compacts them as you described. The code should be pretty self-explanatory, I can't help but feel it should be possible to do it much shorter, but hey.

\documentclass{article}
\makeatletter
\let\dotlessi\i
\newcount\last
\def\range#1{%
\last 0\relax
\def\temp{}
\def\first{}
\@for\i:=#1\do{%
\ifnum\the\last=0%start
\edef\temp{\i}%
\else%
\ifnum\last=\i%
\ifx\first\empty%
\let\@temp\temp%
\edef\temp{\@temp--}%
\def\first{x}%
\else%
\fi%
\else%
\ifx\first\empty%
\let\@temp\temp%
\let\j\i%
\edef\temp{\@temp,\j}
\else%
\let\@temp\temp%
\let\j\i%
\edef\temp{\@temp\the\last,\j}%
\fi%
\fi%
\fi%
\last \i\relax%
}%
[\temp]%
}
\makeatother
\let\i\dotlessi
\begin{document}
\noindent
Some test ranges\\
\range{1,2,3,4,7}\\
\range{1,3,5,7}\\
\range{1,3,4,5,7}\\
\range{}
\end{document}


And the output:

I've moved the \newcount command outside the definition of \range: you were allocating a new counter for each call of \range. – egreg Oct 18 '11 at 12:18