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.

I am working on my PhD Thesis, and need to create an index of all primary sources quoted. I have created "cite" commands so that it automatically adds an index line whenever I cite:

\newcommand{\citeherm}[1]{\emph{Shepherd of Hermas}~#1\index{Shepherd of Hermas!#1}}

This works great, except that the index orders numerals incorrectly. '11' will come between '1' and '2', which is clearly incorrect in this case.

I have attached a minimal example. Who can help?

(BTW I have thought that you could do this by hand by using a running zero that does not print, but I wouldn't know how to automate that, in my command above.

\index{Shepherd of Hermas!03.13–18@3.13–8}

)

Minimal example

\documentclass[12pt]{memoir}
\newcommand{\citeherm}[1]{\emph{Shepherd of Hermas}~#1\index{Shepherd of Hermas!#1}}
\newcommand{\citeash}[1]{\emph{Testament of Asher}~#1\index{Testament of Asher!#1}}
\makeindex

\begin{document}

“bla bla bla” \citeherm{12.1}
“bla bla bla” \citeherm{12.2}
“bla bla bla” \citeherm{12.3}
“bla bla bla” \citeherm{12.4}
“bla bla bla” \citeherm{12.10}
“bla bla bla” \citeherm{12.11}
“bla bla bla” \citeherm{12.20}
“bla bla bla” \citeherm{12.21}


“bla bla bla” \citeash{1.1}
“bla bla bla” \citeash{2.1}
“bla bla bla” \citeash{10.1}
\printindex

\end{document}  
share|improve this question
    
Have you considered using \index{order@text} where order determines the order and text the text to display. This way you could either use an additional counter, if you want them sorted in the order of appearance, or add a 0 in front without it being printed. –  Roelof Spijker Oct 25 '11 at 10:29
add comment

1 Answer

up vote 9 down vote accepted

I assume that your references have the form x.y-z, possibly omitting the -z part, where x, y, and z are numbers less than 100:

\documentclass[12pt]{memoir}

\makeatletter
\def\bruin@normalize#1{\@bruin@normalizei#1-\@nil}
\def\@bruin@normalizei#1.#2-#3\@nil{%
    \two@digits{#1}.\two@digits{#2}%
  \if\relax\detokenize{#3}\relax\else
     -\expandafter\two@digits\expandafter{\@gobblehyphen#3}%
  \fi}
\def\@gobblehyphen#1-{#1}
\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}~##1\index{#2!\bruin@normalize{##1}@##1}}%
}
\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

\makeindex

\begin{document}

bla bla bla \citeherm{12.1}
bla bla bla \citeherm{12.2}
bla bla bla \citeherm{12.3}
bla bla bla \citeherm{12.4}
bla bla bla \citeherm{12.10}
bla bla bla \citeherm{12.11}
bla bla bla \citeherm{12.20}
bla bla bla \citeherm{12.21}


bla bla bla \citeash{1.1}
bla bla bla \citeash{2.1}
bla bla bla \citeash{10.1}
\printindex

\end{document}  

If the format is different, then some corrections have to be made. For example, if the numbers can be three digits long, add

\def\three@digits#1{\ifnum#1<1000 0\ifnum#1<10 0\fi\fi#1}

and use \three@digits instead of \two@digits.

I've also defined a "catch-all" command for defining uniformly the indexing commands.

Added: support for list arguments (and three digits)

\makeatletter
\def\three@digits#1{\ifnum#1<100 0\ifnum#1<10 0\fi\fi#1}
\def\bruin@normalize#1{\@bruin@normalizei#1-\@nil}
\def\@bruin@normalizei#1.#2-#3\@nil{%
    \three@digits{#1}.\three@digits{#2}%
  \if\relax\detokenize{#3}\relax\else
     -\expandafter\three@digits\expandafter{\@gobblehyphen#3}%
  \fi}
\def\@gobblehyphen#1-{#1}
\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}\bruin@dolist{#2}{##1}}}
\def\bruin@dolist#1#2{%
  \toks@={~\@gobbletwo}%
  \@for\next:=#2\do{%
    \edef\next{,\noexpand\space\next
      \noexpand\index{#1!\expandafter\bruin@normalize\expandafter{\next}@\next}}%
    \toks@=\expandafter{\the\expandafter\toks@\next}%
  }\the\toks@
}
\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

Now an input such as \citeherm{12.1,35.4,28.2} will print

Shepherd of Hermas 12.1, 35.4, 28.2

with a nonbreaking space between "Hermas" and the first number; it will index all three references.

Added: support for chapter ranges

\makeatletter
\def\three@digits#1{\ifnum#1<100 0\ifnum#1<10 0\fi\fi#1}
\def\bruin@normalize#1{\@bruin@normalizei#1-.\@nil}
\def\@bruin@normalizei#1.#2-#3.#4\@nil{%
    \three@digits{#1}.\three@digits{#2}%
  \if\relax\detokenize{#4}\relax
     % same chapter range
     \if\relax\detokenize{#3}\relax
       % simple range
       % already set
     \else
       -\expandafter\three@digits\expandafter{\@gobblehyphen#3}%
     \fi
  \else
     % different chapters range
     -\three@digits{#3}.\expandafter\three@digits\expandafter{\@gobblehyphenperiod#4}%
  \fi}
\def\@gobblehyphen#1-{#1}
\def\@gobblehyphenperiod#1-.{#1}

\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}\bruin@dolist{#2}{##1}}}
\def\bruin@dolist#1#2{%
  \toks@={~\@gobbletwo}%
  \@for\next:=#2\do{%
    \edef\next{,\noexpand\space\next
      \noexpand\index{#1!\expandafter\bruin@normalize\expandafter{\next}@\next}}%
    \toks@=\expandafter{\the\expandafter\toks@\next}%
  }\showthe\toks@\the\toks@
}
\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

Now also \citeherm{1.2-3.4} will be indexed. Lists will continue to work as before.

Added: support also for simple citations

\makeatletter
\def\three@digits#1{\ifnum#1<100 0\ifnum#1<10 0\fi\fi#1}
\def\bruin@normalize#1{\@bruin@normalizei#1.\@nil}
\def\@bruin@normalizei#1.#2\@nil{%
  \if\relax\detokenize{#2}\relax
     % only one number
     \three@digits{#1}\expandafter\@gobble
  \else
     \expandafter\@firstofone
  \fi{\@bruin@gobbleperiod#1.#2\@nil}
}
\def\@bruin@gobbleperiod#1.\@nil{\@bruin@normalizeii#1-.\@nil}
\def\@bruin@normalizeii#1.#2-#3.#4\@nil{%
    \three@digits{#1}.\three@digits{#2}%
  \if\relax\detokenize{#4}\relax
     % same chapter range
     \if\relax\detokenize{#3}\relax
       % simple range
       % already set
     \else
       -\expandafter\three@digits\expandafter{\@gobblehyphen#3}%
     \fi
  \else
     % different chapters range
     -\three@digits{#3}.\expandafter\three@digits\expandafter{\@gobblehyphenperiod#4}%
  \fi}
\def\@gobblehyphen#1-{#1}
\def\@gobblehyphenperiod#1-.{#1}

\newcommand{\defcite}[2]{%
  \newcommand{#1}[1]{\emph{#2}\bruin@dolist{#2}{##1}}}
\def\bruin@dolist#1#2{%
  \toks@={~\@gobbletwo}%
  \@for\next:=#2\do{%
    \edef\next{,\noexpand\space\next
      \noexpand\index{#1!\expandafter\bruin@normalize\expandafter{\next}@\next}}%
    \toks@=\expandafter{\the\expandafter\toks@\next}%
  }\the\toks@
}

\makeatother

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

This should allow also citations like \citeherm{12}.

Final (?) addition

Here is a "LaTeX3" implementation:

\documentclass[12pt]{memoir}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \l_bruin_argi_tl
\tl_new:N \l_bruin_argii_tl
\tl_new:N \l_bruin_argx_tl
\tl_new:N \l_bruin_temp_tl
\tl_new:N \l_bruin_final_tl

% First of all we split the comma separated list; we store |#1|, the
% title of the book, in |\bruin_argi_tl| and |\bruin_argx_tl| to
% contain |\use_none:nn| that at the end will gobble the first two
% tokens (a comma and a space); then we put the split list of
% arguments followed by |{\NoValue}| after |\bruin_process:n|, because
% it will be used to terminate the recursion.
\NewDocumentCommand\bruin_dolist:nn {m >{\SplitList{,}}m}{
  \tl_set:Nn \l_bruin_argi_tl { #1 }
  \tl_set:Nn \l_bruin_argx_tl { \use_none:nn }
  \bruin_process:n #2 {\NoValue}
}
% |\bruin_process:n| initiates the recursion; it examines the contents
% of the first pair of braces after it and, if different from
% |\NoValue|, stores it into |\bruin_argii_tl|. Then it appends also
% it to the list |\bruin_argx_tl|, after a comma and a space. The same
% contents is also processed by |\bruin_normalize:n|. Then we execute
% |\bruin_do:| and call again |\bruin_process:n| to restart the recursion.
\cs_new:Nn \bruin_process:n {
  \IfNoValueTF{#1}
    {}% end of recursion
    {
     \tl_set:Nn \l_bruin_argii_tl {#1}
     \tl_set:Nx \l_bruin_argx_tl { \l_bruin_argx_tl , \c_space_tl #1 }
     \bruin_normalize:n {#1} \bruin_do: \bruin_process:n
    }
}
% |\bruin_normalize:n| is responsible for splitting an entry of the
% form |A-B| (|A| and |B| will actually be something like |1.2|) into
% the two components, which are passed to |\bruin_normalizei:nn|
\NewDocumentCommand\bruin_normalize:n {>{\SplitArgument{1}{-}}m}{
  \bruin_normalizei:nn #1
}
% If the entry was |A-B|, |\bruin_normalizei:nn| is passed |{A}{B}|,
% otherwise it is passed |{A}{\NoValue}|
\cs_new:Nn \bruin_normalizei:nn {
  \IfNoValueTF{#2}
    {\bruin_three:n {#1}} % simple entry
    {\bruin_three:nn {#1}{#2}} % complex entry
}
% |\bruin_three:n| splits an argument of the form |1.2.3| into the
% components; it's quite similar to |\bruin_normalize:n|
\NewDocumentCommand\bruin_three:n {>{\SplitArgument{2}{.}}m}{
  \bruin_threedo:nnn #1
  \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl}
}
% |\bruin_threedo:nnn| sets |\l_bruin_final_tl| to the components but
% with three digits; |1| is transformed into |001| and |1.2| becomes
% |001.002|, while |1.2.3| becomes |001.002.003|
\cs_new:Nn \bruin_threedo:nnn {
  \IfNoValueTF{#3}
    {\IfNoValueTF{#2}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}}}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}}}
    }
    {\tl_set:Nx \l_bruin_temp_tl
       {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}.\bruin_threedig:n{#3}}
    }
}
% we do similarly for |\bruin_three:nn|, calling |\bruin_threedo:nnn| twice.
\NewDocumentCommand\bruin_three:nn
  {>{\SplitArgument{2}{.}}m >{\SplitArgument{2}{.}}m}
  { \bruin_threedo:nnn #1
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl }
    \bruin_threedo:nnn #2
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_final_tl -- \l_bruin_temp_tl}
  }

\cs_new:Nn \bruin_do: {
  \cs_set:Nx \bruin_next: {
    \exp_not:N \index 
      {\l_bruin_argi_tl ! \l_bruin_final_tl \c_space_tl @ \l_bruin_argii_tl }
  }
  \bruin_next: 
}
\cs_new:Nn \bruin_threedig:n {
  \int_compare:nNnTF {#1} < {100}
    { 0 \int_compare:nNnTF {#1} < {10} {0}{} }
    {} #1
}

% |\bruin_defcite:nn| defines |#1| as a macro with one argument, first
% storing its second argument in it. So
%
% |\defcite{\citeT}{Title}|
%
% defines |\citeT| as if it were
%
% |\def\citeT#1{\emph{Title}\space\bruin_dolist:nn{Title}{#1}\l_bruin_argx_tl}|
%
% The token list |\l_bruin_argx_tl| will be built by |\bruin_dolist:nn|
\cs_new:Nn \bruin_defcite:nn {
  \cs_new:Npn #1##1{\emph{#2}\space\bruin_dolist:nn {#2}{##1} \l_bruin_argx_tl }}
\cs_set_eq:NN \defcite \bruin_defcite:nn
\ExplSyntaxOff

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

\makeindex

\begin{document}

%\tracingmacros=1
bla bla bla \citeherm{12.1-2}
\newpage
bla bla bla \citeherm{12.2,13.9}
\newpage
bla bla bla \citeherm{12.3-13.4}
\newpage
bla bla bla \citeherm{12}
\newpage
bla bla bla \citeherm{12.10}
\newpage
bla bla bla \citeherm{12.11}
\newpage
bla bla bla \citeherm{12.20}
\newpage
bla bla bla \citeherm{12.21}
\newpage


bla bla bla \citeash{1.2.3}
\newpage
bla bla bla \citeash{2.1}.
\newpage
bla bla bla \citeash{10.1,1.1,2.9-19}.
\newpage
\printindex

\end{document}

This should support also three levels, as you see in the \citeash example.


Version after the July 2012 update of LaTeX3

\documentclass[12pt]{memoir}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \l_bruin_argi_tl
\tl_new:N \l_bruin_argii_tl
\tl_new:N \l_bruin_argx_tl
\tl_new:N \l_bruin_temp_tl
\tl_new:N \l_bruin_final_tl

% First of all we split the comma separated list; we store |#1|, the
% title of the book, in |\bruin_argi_tl| and |\bruin_argx_tl| to
% contain |\use_none:nn| that at the end will gobble the first two
% tokens (a comma and a space); then we put the split list of
% arguments followed by |{\NoValue}| after |\bruin_process:n|, because
% it will be used to terminate the recursion.
\NewDocumentCommand\bruin_dolist:nn {m >{\SplitList{,}}m}{
  \tl_set:Nn \l_bruin_argi_tl { #1 }
  \tl_set:Nn \l_bruin_argx_tl { \use_none:nn }
  \ProcessList{#2}{\bruin_process:n}
}
% |\bruin_process:n| does the recursion via \ProcessList; it
% stores its argument into |\bruin_argii_tl|. Then it appends also
% it to the list |\bruin_argx_tl|, after a comma and a space. The same
% contents is also processed by |\bruin_normalize:n|. Then we execute
% |\bruin_do:|.
\cs_new:Nn \bruin_process:n {
     \tl_set:Nn \l_bruin_argii_tl {#1}
     \tl_set:Nx \l_bruin_argx_tl { \l_bruin_argx_tl , \c_space_tl #1 }
     \bruin_normalize:n {#1} \bruin_do: 
}
% |\bruin_normalize:n| is responsible for splitting an entry of the
% form |A-B| (|A| and |B| will actually be something like |1.2|) into
% the two components, which are passed to |\bruin_normalizei:nn|
\NewDocumentCommand\bruin_normalize:n {>{\SplitArgument{1}{-}}m}{
  \bruin_normalizei:nn #1
}
% If the entry was |A-B|, |\bruin_normalizei:nn| is passed |{A}{B}|,
% otherwise it is passed |{A}{\NoValue}|
\cs_new:Nn \bruin_normalizei:nn {
  \IfNoValueTF{#2}
    {\bruin_three:n {#1}} % simple entry
    {\bruin_three:nn {#1}{#2}} % complex entry
}
% |\bruin_three:n| splits an argument of the form |1.2.3| into the
% components; it's quite similar to |\bruin_normalize:n|
\NewDocumentCommand\bruin_three:n {>{\SplitArgument{2}{.}}m}{
  \bruin_threedo:nnn #1
  \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl}
}
% |\bruin_threedo:nnn| sets |\l_bruin_final_tl| to the components but
% with three digits; |1| is transformed into |001| and |1.2| becomes
% |001.002|, while |1.2.3| becomes |001.002.003|
\cs_new:Nn \bruin_threedo:nnn {
  \IfNoValueTF{#3}
    {\IfNoValueTF{#2}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}}}
       {\tl_set:Nx \l_bruin_temp_tl
          {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}}}
    }
    {\tl_set:Nx \l_bruin_temp_tl
       {\bruin_threedig:n{#1}.\bruin_threedig:n{#2}.\bruin_threedig:n{#3}}
    }
}
% we do similarly for |\bruin_three:nn|, calling |\bruin_threedo:nnn| twice.
\NewDocumentCommand\bruin_three:nn
  {>{\SplitArgument{2}{.}}m >{\SplitArgument{2}{.}}m}
  { \bruin_threedo:nnn #1
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_temp_tl }
    \bruin_threedo:nnn #2
    \tl_set:Nx \l_bruin_final_tl {\l_bruin_final_tl -- \l_bruin_temp_tl}
  }

\cs_new:Nn \bruin_do: {
  \cs_set:Nx \bruin_next: {
    \exp_not:N \index 
      {\l_bruin_argi_tl ! \l_bruin_final_tl \c_space_tl @ \l_bruin_argii_tl }
  }
  \bruin_next: 
}
\cs_new:Nn \bruin_threedig:n {
  \int_compare:nNnTF {#1} < {100}
    { 0 \int_compare:nNnTF {#1} < {10} {0}{} }
    {} #1
}

% |\bruin_defcite:nn| defines |#1| as a macro with one argument, first
% storing its second argument in it. So
%
% |\defcite{\citeT}{Title}|
%
% defines |\citeT| as if it were
%
% |\def\citeT#1{\emph{Title}\space\bruin_dolist:nn{Title}{#1}\l_bruin_argx_tl}|
%
% The token list |\l_bruin_argx_tl| will be built by |\bruin_dolist:nn|
\cs_new:Nn \bruin_defcite:nn {
  \cs_new:Npn #1##1{\emph{#2}\space\bruin_dolist:nn {#2}{##1} \l_bruin_argx_tl }}
\cs_set_eq:NN \defcite \bruin_defcite:nn
\ExplSyntaxOff

\defcite{\citeherm}{Shepherd of Hermas}
\defcite{\citeash}{Testament of Asher}

\makeindex

\begin{document}

%\tracingmacros=1
bla bla bla \citeherm{12.1-2}
\newpage
bla bla bla \citeherm{12.2,13.9}
\newpage
bla bla bla \citeherm{12.3-13.4}
\newpage
bla bla bla \citeherm{12}
\newpage
bla bla bla \citeherm{12.10}
\newpage
bla bla bla \citeherm{12.11}
\newpage
bla bla bla \citeherm{12.20}
\newpage
bla bla bla \citeherm{12.21}
\newpage


bla bla bla \citeash{1.2.3}
\newpage
bla bla bla \citeash{2.1}.
\newpage
bla bla bla \citeash{10.1,1.1,2.9-19}.
\newpage
\printindex

\end{document}
share|improve this answer
    
Whoah, this is awesome. Just, ranges don't appear to work. For example "“bla bla bla” \citeherm{12.1-2}" gives an error "! Missing = inserted for \ifnum." –  Tom de Bruin Oct 25 '11 at 11:43
    
@TomdeBruin You're right; I've edited the answer –  egreg Oct 25 '11 at 12:32
    
Is there any way to expand this to accept the following \citeherm{12.1, 35.4, 28.2}? –  Tom de Bruin Oct 25 '11 at 14:51
    
@TomdeBruin See edited answer –  egreg Oct 25 '11 at 16:59
1  
@TomdeBruin Maybe the last? :) This has also book.chapter.verse. You owe me a beer. –  egreg Nov 23 '11 at 21:37
show 16 more comments

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.