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.

4
  • 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. Commented Dec 7, 2018 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. Commented Dec 7, 2018 at 20:06
  • You have to add delim_1 ", \\checkrange{" and delim_2 ", \\checkrange{" to the .ist file. Commented Dec 7, 2018 at 21:38
  • I added the missing part. Commented Dec 7, 2018 at 23:16

1 Answer 1

3

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

4
  • 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? Commented Jul 20, 2019 at 8:42
  • 1
    @Erika Such problems usually are due to not up-to-date TeX distribution Commented Jul 20, 2019 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?). Commented Jul 24, 2019 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/… Commented Jul 30, 2019 at 15:14

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.