# Suppress redundant page numbers in the toc

When there is a succession of short sections, or chapter-to-section changes with no text in between, like in here,

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}


I end up having a toc that looks like this:

My question is, is it possible to tell the toc not to display anything unless the page number have changed ? In order to get something like:

Or alternatively, get the redundant page numbers in a shade of gray, like

EDIT: All of the solutions work perfectly fine on their respective MWE, but not for my thesis. It will take me some time to find which package is responsible for this amoung the tens that are loaded. Heiko's solutions are the one producing the best results in my thesis, but I have the following problem on a new toc page:

The labels are back to normal, and this do not happen on the MWE.

• I wouldn't do this. Imagine the poor reader if, for example, Section2 entry is on page 1 of the ToC but Section 3 entry is on page 2. He/She has to turn back the page to find out the page for Section3. Sep 8, 2015 at 18:22
• Good point. Another option is to colour redundant numbers in gray. I will edit the post to take this option into account.
– HcN
Sep 8, 2015 at 18:24
• The class and or packages, which influence the table of contents should be added to the MWE. Sep 8, 2015 at 20:56
• @HeikoOberdiek Those which influences the toc are in already. I wrote a MWE with all the packages and all the same surrounding around the \tableofcontents entry, and yet I could not reproduce this error. I am certainly missing something, but it is unlikely that this error will reproduce in the actual thesis as these chapters should be rather long.
– HcN
Sep 9, 2015 at 12:56

See ADDENDUM for hyperref version.

Here's a tocloft solution. I patch the definitions of \cftXfillnum so that #1 is replaced with \clrize{#1}. Then I define \clrize to check if #1 matches the globally defined previous page entry. If so, it makes it gray, otherwise, it leaves it black and updates the globally saved page entry.

EDITED to use \xpatchcmd instead of specifying a full \renewcommand.

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{tocloft,xcolor,xpatch}
\xpatchcmd\cftchapfillnum{#1}{\clrize{#1}}{}{}
\xpatchcmd\cftsecfillnum{#1}{\clrize{#1}}{}{}
\def\svpage{0}
\def\clrize#1{\ifnum#1=\svpage\relax\textcolor{black!20}{#1}\else#1\gdef\svpage{#1}\fi}
\usepackage{lipsum}
\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\lipsum[6]
\section{Section 4}
\lipsum[7]
\end{document}


The OP asked about hyperref compatibility. After thrashing about a bit, I discovered that #1 to the \cftXfillnum macros was not a number, but instead a hyperref command of some sort. I therefore had to parse that command in order to extract the page number as argument #4 of that new command.

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{tocloft,xcolor,xpatch}
\usepackage{lipsum}
\xpatchcmd\cftchapfillnum{#1}{\clrize#1\relax#1}{}{}
\xpatchcmd\cftsecfillnum{#1}{\clrize#1\relax#1}{}{}
\def\svpage{0}
\def\clrize#1#2#3#4#5\relax{\ifnum#4=\svpage\relax%
\color{black!30}\else\gdef\svpage{#4}\fi}
\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\lipsum[6]
\section{Section 4}
\lipsum[1-42]
\section{Section 5}
\lipsum[7]
\end{document}


• Thanks a lot. Your solution do is elegant, but for some reason it is not happy cohabiting with \usepackage[linktoc=all]{hyperref}. Is there something that can be done ?
– HcN
Sep 8, 2015 at 19:47
• It works on the MWE, but it seems not to work anymore when they are parts or subsections e.g. if we add after \tableofcontents the code \part{Part} \chapter{Chapter 1}\lipsum[1] \section{Section 1} \subsection{Subsection 1}
– HcN
Sep 9, 2015 at 15:02
• @HcN You have to add a line like \xpatchcmd\cftXfillnum{#1}{\clrize#1\relax#1}{}{} for each sectioning unit you wish to support, where I have shown, in my MWE, X = chap and sec. You will need to add lines for part, subsec, subsubsec, para, etc., as needed. Looking at the tocloft package documentation might help. Sep 9, 2015 at 15:12
• Indeed, as simple as that. Thanks a lot indeed.
– HcN
Sep 9, 2015 at 15:28

LaTeX uses \l@<section> for formatting an entry in the table of contents. The second argument is the page number. Thus, it can be defined as macro and compared in the next call.

The following example also works with hyperref.

Also only those \l@... commands should be redefined, that actually appear in the table of contents. Otherwise a page number, which would appear on a section level, which is not put into the table of contents, would not be visible for the next entry on the same page, that is in the table of contents.

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}

\makeatletter
\def\toc@page{}

% Chapters should have a page number.
% \l@chapter is redefined to remember the page number
\let\saved@l@chapter\l@chapter
\renewcommand*{\l@chapter}[2]{%
\def\toc@page{#2}%
\saved@l@chapter{#1}{#2}%
}

\let\saved@l@section\l@section
\renewcommand*{\l@section}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\def\toc@page{#2}%
\expandafter\@secondoftwo
\fi
{%
\saved@l@section{#1}{}%
}{%
\saved@l@section{#1}{#2}%
}%
}

\let\saved@l@subsection\l@subsection
\renewcommand*{\l@subsection}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\saved@l@subsection{#1}{}%
}{%
\saved@l@subsection{#1}{#2}%
}%
}
\makeatother

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\subsection{Subsection A}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}


# With gray page numbers

Making the page numbers gray instead, only requires a small modification:

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}
\usepackage{color}
\definecolor{tocpagegray}{gray}{.5}

\makeatletter
\def\toc@page{}

% Chapters should have a page number.
% \l@chapter is redefined to remember the page number
\let\saved@l@chapter\l@chapter
\renewcommand*{\l@chapter}[2]{%
\def\toc@page{#2}%
\saved@l@chapter{#1}{#2}%
}

\let\saved@l@section\l@section
\renewcommand*{\l@section}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\def\toc@page{#2}%
\expandafter\@secondoftwo
\fi
{%
\saved@l@section{#1}{\textcolor{tocpagegray}{#2}}%
}{%
\saved@l@section{#1}{#2}%
}%
}

\let\saved@l@subsection\l@subsection
\renewcommand*{\l@subsection}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\saved@l@subsection{#1}{\textcolor{tocpagegray}{#2}}%
}{%
\saved@l@subsection{#1}{#2}%
}%
}
\makeatother

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\subsection{Subsection A}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}


# With gray page numbers and gray dots

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}
\usepackage{color}
\definecolor{tocpagegray}{gray}{.5}

\makeatletter
\def\toc@page{}

% Chapters should have a page number.
% \l@chapter is redefined to remember the page number
\let\saved@l@chapter\l@chapter
\renewcommand*{\l@chapter}[2]{%
\def\toc@page{#2}%
\saved@l@chapter{#1}{#2}%
}

\let\saved@l@section\l@section
\renewcommand*{\l@section}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\def\toc@page{#2}%
\expandafter\@secondoftwo
\fi
{%
\begingroup
\saved@l@section{#1}{\textcolor{tocpagegray}{#2}}%
\endgroup
}{%
\saved@l@section{#1}{#2}%
}%
}

\let\saved@l@subsection\l@subsection
\renewcommand*{\l@subsection}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\begingroup
\saved@l@subsection{#1}{\textcolor{tocpagegray}{#2}}%
\endgroup
}{%
\saved@l@subsection{#1}{#2}%
}%
}

}
\makeatother

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\subsection{Subsection A}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}


# With removal of unnecessary dots

If the page number is omitted, then the dots leading to the non-existent page number are no longer necessary. The following example removes them. Package hyperref is loaded this time.

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}
\usepackage{etoolbox}

\makeatletter
\def\toc@page{}

% Chapters should have a page number.
% \l@chapter is redefined to remember the page number
\let\saved@l@chapter\l@chapter
\renewcommand*{\l@chapter}[2]{%
\def\toc@page{#2}%
\saved@l@chapter{#1}{#2}%
}

\let\saved@l@section\l@section
\renewcommand*{\l@section}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\def\toc@page{#2}%
\expandafter\@secondoftwo
\fi
{%
\begingroup
\saved@l@section{#1}{}%
\endgroup
}{%
\saved@l@section{#1}{#2}%
}%
}

\let\saved@l@subsection\l@subsection
\renewcommand*{\l@subsection}[2]{%
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\begingroup
\saved@l@subsection{#1}{}%
\endgroup
}{%
\saved@l@subsection{#1}{#2}%
}%
}
\makeatother

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\subsection{Subsection A}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}


# With unnecessary dots and with page numbers at new pages

The following modification sets labels to detect the page, where the entry of the table of contents is located. Then it checks, if the page number has changed, which indicates a new page in the table of contents. Then the page number is set, even if the page number already appears on the previous page.

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}
\usepackage{etoolbox}

\usepackage{zref-abspage}

\makeatletter
\def\toc@page{}
\def\toc@abspage{}
\newcounter{toc@abspage}
\renewcommand*{\thetoc@abspage}{%
ta@\the\value{toc@abspage}%
}
\newcommand*{\toc@abspage@label}{%
\zref@labelbyprops{\thetoc@abspage}{abspage}%
}
\newcommand*{\toc@abspage@check}{%
\stepcounter{toc@abspage}%
\edef\toc@abs@page{\zref@extractdefault{\thetoc@abspage}{abspage}{}}%
\zref@refused{\thetoc@abspage}%
\ifx\toc@abs@page\toc@abspage
\else
\def\toc@page{}%
\let\toc@abspage\toc@abs@page
\fi
}

% Chapters should have a page number.
% \l@chapter is redefined to remember the page number
\let\saved@l@chapter\l@chapter
\renewcommand*{\l@chapter}[2]{%
\toc@abspage@check
\def\toc@page{#2}%
\saved@l@chapter{#1}{#2\toc@abspage@label}%
}

\let\saved@l@section\l@section
\renewcommand*{\l@section}[2]{%
\toc@abspage@check
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\def\toc@page{#2}%
\expandafter\@secondoftwo
\fi
{%
\begingroup
\saved@l@section{#1}{\toc@abspage@label}%
\endgroup
}{%
\saved@l@section{#1}{#2\toc@abspage@label}%
}%
}

\let\saved@l@subsection\l@subsection
\renewcommand*{\l@subsection}[2]{%
\toc@abspage@check
\def\toc@new@page{#2}%
\ifx\toc@new@page\toc@page
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\begingroup
\saved@l@subsection{#1}{\toc@abspage@label}%
\endgroup
}{%
\saved@l@subsection{#1}{#2\toc@abspage@label}%
}%
}
\makeatother

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\subsection{Subsection A}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}

• Many thanks (again). It works perfectly, but as suggested, the toc does not look great and is fairly misleading. Is there a way to modify your code to colour redundant page numbers in gray ? (See the edited question).
– HcN
Sep 8, 2015 at 18:32
• @HcN The version without dots would be an alternative. Sep 8, 2015 at 18:52
• Your code seems the one that gives the best results on my thesis, although I still have a tiny problem on a new page - maybe a subsection thing. (I did edit the post)
– HcN
Sep 8, 2015 at 20:37

You can try this:

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}

\newcount\tocpgnum
\let\contentslineori=\contentsline
\def\contentsline#1#2#3{%
\ifnum\tocpgnum=#3
\contentslineori{#1}{#2}{}%
\else
\contentslineori{#1}{#2}{#3}%
\tocpgnum=#3
\fi
}

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}

• A LaTeX answer? This is new! (at least for me). I up-vote your plain answers, bu this one I won't. (Just kidding). Sep 8, 2015 at 18:41
• Add after \lipsum[7]: \newpage\subsubsection{foo}\section{foo}. The subsubsection "foo" would get the visible page number "4", but it is not included in the table of contents. The next section on the same page does not know this and removes the page number, because it thinks, the page is already printed in the previous entry. Sep 8, 2015 at 18:50

Here is to show how to handle this with package etoc.

For simplicity I have used the technique from the etoc manual section another compatibility mode; else one would need to set up more complete styles (for the case of gray leader dots one can do it as here copying the \toc@leaders@gray method from Heiko's answer). Example of setting up etoc styles are given in the manual, for example, Part I, section 6 A second example.

\documentclass[10pt,a4paper]{report}
\usepackage[utf8]{inputenc}
\usepackage{lipsum}
\usepackage{hyperref}
\usepackage{etoc}
\usepackage{color}
\definecolor{tocpagegray}{gray}{.5}

\makeatletter

\let\savedchapterline\l@chapter
\let\savedsectionline\l@section
\let\savedsubsectionline\l@subsection

\def\saved@previouspage {0}

\etocsetstyle{chapter}
{}
{\xdef\HcN@zz{\etocthepage}}
{\ifx\HcN@zz\saved@previouspage
\savedchapterline{\numberline{\etocnumber}\etocname}
{\textcolor{tocpagegray}{\etocpage}}%
\else
\savedchapterline{\numberline{\etocnumber}\etocname}
{\etocpage}%
\fi
\global\let\saved@previouspage\HcN@zz
}
{}%

\etocsetstyle{section}
{}
{\xdef\HcN@zz{\etocthepage}}
{\ifx\HcN@zz\saved@previouspage
\savedsectionline{\numberline{\etocnumber}\etocname}
{\textcolor{tocpagegray}{\etocpage}}%
\else
\savedsectionline{\numberline{\etocnumber}\etocname}
{\etocpage}%
\fi
\global\let\saved@previouspage\HcN@zz
}
{}%

\etocsetstyle{subsection}
{}
{\xdef\HcN@zz{\etocthepage}}
{\ifx\HcN@zz\saved@previouspage
\savedsubsectionline{\numberline{\etocnumber}\etocname}
{\textcolor{tocpagegray}{\etocpage}}%
\else
\savedsubsectionline{\numberline{\etocnumber}\etocname}
{\etocpage}%
\fi
\global\let\saved@previouspage\HcN@zz
}
{}

\makeatother

\begin{document}
\tableofcontents

\chapter{Chapter 1}
\lipsum[1]
\section{Section 1}
\lipsum[2]
\section{Section 2}
\lipsum[3-5]
\section{Section 3}
\subsection{Subsection A}
\lipsum[6]
\section{Section 4}
\lipsum[7]

\end{document}


• Works great, thanks ! It has the side effect to increase the gap around the "part" entries in the toc. Is it related to etoc ?
– HcN
Sep 9, 2015 at 12:53
• can you try with the addition of \etocsetstyle{part}{}{}{\savedpartline{\etocnumber\hspace{1em}\etocname}{\etocpage}}{}, after having done \let\savedpartline\l@part, all of that in the preamble code between \makeatletter and \makeatother (assuming you are using book or article class).
– user4686
Sep 9, 2015 at 13:11
• and yes the increased gap is probably related to etoc in such a case because the style used for Parts is the custom one of etoc, not the default provided by the document class. My previous comment shows how to reproduce this default. And actually that bit of code should be extended to look like the chapter, section, subsection settings in my code with the \ifx.. conditional, etc...
– user4686
Sep 9, 2015 at 13:15
• Miktek returns '! Undefined control sequence. <argument> \etocp ‌​age' once the toc is compiled. The gap is indeed reduced, but the it displays the word "age" in place of the page number. Using either the book or article class does not help.
– HcN
Sep 9, 2015 at 15:10
• @HcN it appears you have a spurious space, yes I also get such an extra space from copying pasting the code in my comment above; after pasting (perhaps in some good text editor) make sure to suppress the space after the p letter. It should be \etocpage not \etocp age.
– user4686
Sep 9, 2015 at 16:07