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}
\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 a0in front without it being printed. – Roelof Spijker Oct 25 '11 at 10:29