2

This is a followup question to egreg's answer to Can one truncate page ranges in index using splitidx? The answer runs into a problem if an index topic occurs in more than one page range. The following MWE, based on egreg's answer:

\begin{filecontents*}{subjind.ist}
delim_0 ", \\checkrange{"
delim_t "}"
\end{filecontents*}

\documentclass{book}
\usepackage{xparse}
\usepackage{imakeidx}

\makeindex[title=Subject Index,options=-s subjind]

\ExplSyntaxOn
\NewDocumentCommand{\checkrange}{>{\SplitArgument{1}{--}}m}
 {
  \formatrange#1
 }
\NewDocumentCommand{\formatrange}{mm}
 {
  \IfNoValueTF { #2 }
   { % no --, it's not a range
    #1
   }
   { % it's a range
    \egreg_range:nn { #1 } { #2 }
   }
 }

\bool_new:N \l__egreg_range_compare_bool

\cs_new_protected:Nn \egreg_range:nn
 {
  % print the start number
  #1--
  \int_compare:nNnTF { \str_count:n { #1 } } = { \str_count:n { #2 } }
   {% same number of digits, remove equal ones at the start
    \__egreg_range_compare:nn { #1 } { #2 }
   }
   {% different number of digits, print both
    #2
   }
 }
\cs_new_protected:Nn \__egreg_range_compare:nn
 {
  \bool_set_true:N \l__egreg_range_compare_bool
  % do a loop on the digits in the first number
  \int_step_inline:nn { \str_count:n { #1 } }
   {
    \bool_if:NTF \l__egreg_range_compare_bool
     {
      % if the digits coincide, print nothing
      \str_if_eq:eeF { \str_item:nn { #1 } { ##1 } } { \str_item:nn { #2 } { ##1 } }
       {
        \str_item:nn { #2 } { ##1 }
        % at the first differing digit, don't compare any longer
        \bool_set_false:N \l__egreg_range_compare_bool
       }
     }
     { \str_item:nn { #2 } { ##1 } }
   }
 }
\ExplSyntaxOff

\begin{document}
\setcounter{page}{95}\index{Topic0|(}Text\newpage
\setcounter{page}{101}\index{Topic0|)}Text\newpage
\setcounter{page}{124}\index{Topic1|(}\index{Topic2|(}Text\newpage
\setcounter{page}{126}\index{Topic1|)}Text\newpage
\setcounter{page}{134}\index{Topic2|)}Text\newpage
\setcounter{page}{141}\index{Topic0}Text\newpage
\index{Topic0}Text\newpage
\index{Topic0}Text
\printindex
\end{document}

yields the following error message:

! LaTeX3 Error: Too many '--' tokens when trying to split argument.

For immediate help type H <return>.
 ...                                              

l.3   \item Topic0, \checkrange{95--101, 141--143}

Entering non-stop yields the following output:

enter image description here

which omits "-3" from the second index entry of Topic0. The question is how to get around this.

Edit following up on egreg's ingenious solution. I don't know if I should ask a new follow up question, so I'm posing it first here in this edit. egreg's solution doesn't seem to handle index sub-topics. If one adds the following 4 lines

\setcounter{page}{135}\index{Topic0!sub0|(}Text\newpage
\setcounter{page}{136}\index{Topic0!sub0!subsub0|(}Text\newpage
\setcounter{page}{139}\index{Topic0!sub0!subsub0|)}Text\newpage
\setcounter{page}{140}\index{Topic0!sub0|)}Text\newpage

after

\setcounter{page}{134}\index{Topic2|)}Text\newpage

in the solution, one gets the following errors:

Output written in mwe.ind.
Transcript written in mwe.ilg.
 (mwe.ind
! Extra }, or forgotten \endgroup.
l.4     \subitem sub0, 135--140}

? r
OK, entering \nonstopmode...
! Extra }, or forgotten \endgroup.
l.5       \subsubitem subsub0, 136--139}

) [145] (mwe.aux) )

and this output:

enter image description here

Alas I don't understand egreg's code well enough to make the required changes.

  • Please don't pile up questions in questions. You started with page ranges, not mentioning other types of indexing and now subitems pop out. Without a full specification, it is impossible to guess what you really need. – egreg Dec 7 '18 at 18:43
  • @egreg: my apologies, when I originally asked the ranges question, I didn't think to specify that the index I'm writing had subitems. Should I ask another question? In any case your solutions are very helpful, it's just that I hadn't thought about the additional complications with subitems. – ss1789 Dec 7 '18 at 20:06
  • You have to add delim_1 ", \\checkrange{" and delim_2 ", \\checkrange{" to the .ist file. – egreg Dec 7 '18 at 21:38
  • I added the missing part. – egreg Dec 7 '18 at 23:16
4

Here it is:

\begin{filecontents*}{subjind.ist}
delim_0 ", \\checkrange{"
delim_1 ", \\checkrange{"
delim_2 ", \\checkrange{"
delim_t "}"
\end{filecontents*}

\documentclass{book}
\usepackage{xparse}
\usepackage{imakeidx}

\makeindex[title=Subject Index,options=-s subjind]

\ExplSyntaxOn
\NewDocumentCommand{\checkrange}{m}
 {
  \seq_set_from_clist:Nn \l__ssind_in_seq { #1 }
  \seq_set_map:NNn \l__ssind_out_seq \l__ssind_in_seq { \checkrangeaux{##1} }
  \seq_use:Nn \l__ssind_out_seq {,~}
 }
\seq_new:N \l__ssind_in_seq
\seq_new:N \l__ssind_out_seq

\NewDocumentCommand{\checkrangeaux}{>{\SplitArgument{1}{--}}m}
 {
  \formatrange#1
 }
\NewDocumentCommand{\formatrange}{mm}
 {
  \IfNoValueTF { #2 }
   { % no --, it's not a range
    #1
   }
   { % it's a range
    \egreg_range:nn { #1 } { #2 }
   }
 }

\bool_new:N \l__egreg_range_compare_bool

\cs_new_protected:Nn \egreg_range:nn
 {
  % print the start number
  #1--
  \int_compare:nNnTF { \str_count:n { #1 } } = { \str_count:n { #2 } }
   {% same number of digits, remove equal ones at the start
    \__egreg_range_compare:nn { #1 } { #2 }
   }
   {% different number of digits, print both
    #2
   }
 }
\cs_new_protected:Nn \__egreg_range_compare:nn
 {
  \bool_set_true:N \l__egreg_range_compare_bool
  % do a loop on the digits in the first number
  \int_step_inline:nn { \str_count:n { #1 } }
   {
    \bool_if:NTF \l__egreg_range_compare_bool
     {
      % if the digits coincide, print nothing
      \str_if_eq:eeF { \str_item:nn { #1 } { ##1 } } { \str_item:nn { #2 } { ##1 } }
       {
        \str_item:nn { #2 } { ##1 }
        % at the first differing digit, don't compare any longer
        \bool_set_false:N \l__egreg_range_compare_bool
       }
     }
     { \str_item:nn { #2 } { ##1 } }
   }
 }
\ExplSyntaxOff

\begin{document}
\setcounter{page}{95}\index{Topic0|(}Text\newpage
\setcounter{page}{101}\index{Topic0|)}Text\newpage
\setcounter{page}{124}\index{Topic1|(}\index{Topic2|(}Text\newpage
\setcounter{page}{126}\index{Topic1|)}Text\newpage
\setcounter{page}{134}\index{Topic2|)}Text\newpage
\setcounter{page}{135}\index{Topic0!sub0|(}Text\newpage
\setcounter{page}{136}\index{Topic0!sub0!subsub0|(}Text\newpage
\setcounter{page}{139}\index{Topic0!sub0!subsub0|)}Text\newpage
\setcounter{page}{140}\index{Topic0!sub0|)}Text\newpage
\setcounter{page}{141}\index{Topic0}Text\newpage
\index{Topic0}Text\newpage
\index{Topic0}Text
\printindex
\end{document}

Now \checkrange splits its input at commas and does the processing as before to every item. This also copes with subitems.

enter image description here

  • I'm trying to compile the MWE you've provided here (straight copy-paste) but I'm getting an Undefined control sequence error in l.3 of the .ind file... In my own MWE where I borrowed your code (see tex.stackexchange.com/questions/500636/…) it runs okay, but the page numbers aren't affected and still appear in the full range. Any idea what's going on? – Erika Jul 20 at 8:42
  • 1
    @Erika Such problems usually are due to not up-to-date TeX distribution – egreg Jul 20 at 9:16
  • I've sorted that out, but the solution seems to break if there are any "see" or "see also" entries--can that be resolved (or should I ask a separate question?). – Erika Jul 24 at 11:22
  • For the record, I ended up asking a separate question regarding this solution and "see (also)", here: tex.stackexchange.com/questions/501382/… – Erika Jul 30 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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