[Tex/LaTex] Problem using \noopsort to sort on ‘year’ using natbib

bibtexnatbib

In my efforts to familiarise myself with BIBTeX and natbib, I've been playing with the xampl.bib database which is shipped with the TeXLive 2011 distribution. I created a document (using the plainnat.bststyle) which cited every entry in the database. Compiling the document produced console output as follows (after running PDFLaTeX for the third time):

(./xampl-citations.tex
! Missing = inserted for \ifnum.
<to be read again>
                   \def
l.15 {\citeref{book-full}
                         }
?

\citeref is a command I define so that I can change the citation style easily. It appears as follows in the document preamble:

\newcommand{\refcolor}{\color{Black}}
\newcommand{\citeref}{\refcolor \citep}

After a process of elimination (ie by changing entries in the xampl.bib database using JabRef), I've pinned the problem down to entries in the 'year' field of the following form:

{\noopsort{1973c}}1981

As far as I can tell \noopsort is correctly defined in the preamble of the xampl.bib file:

@PREAMBLE{ "\newcommand{\noopsort}[1]{} "
# "\newcommand{\printfirst}[2]{#1} "
# "\newcommand{\singleletter}[1]{#1} "
# "\newcommand{\switchargs}[2]{#2#1} " }

As you might imagine, I've done a fair amount of searching around to see if anyone else has reported a similar problem, and I've found a question on this resource which suggests that the problem might be an excess of curly brackets in the .bib file:

natbib-error-when-citing-paper-with-similar-authors-and-same-year

Indeed, an example section from my xampl.bib file does suggest that the entries contain a superfluous pair of curly brackets:

@BOOK{book-full,
  title = {Seminumerical Algorithms},
  publisher = {Addison-Wesley},
  year = {{\noopsort{1973c}}1981},
  author = {Donald E. Knuth},
  volume = {2},
  series = {The Art of Computer Programming},
  address = {Reading, Massachusetts},
  edition = {Second},
  month = {10~} # jan,
  note = {This is a full BOOK entry},
  fileno = {91}
}

The problem is – what do I do about it? The way my set-up works is that I hold my master database in EndNote and then export entries (required fields only) into JabRef where I perform the necessary tweaks for LaTeX. This system works well for me, so I don't particularly want to change it. However, if surplus curly brackets is the cause of the problem, then how do I prevent them appearing in the .bib file?

This is not a hypothetical question, because I will need occasionally to adjust the sort order of entries in my thesis bibliography – so I need to find a way to do it. If the \noopsort method cannot be made to work, are there any other options?

Best Answer

The problem is the supplementary pair of braces, which is however necessary for correct sorting. You should try biblatex, really, because it provides different fields for sorting dates.

A possible patch is to write, after loading natbib,

\makeatletter
\let\NAT@bare@aux\NAT@bare
\def\NAT@bare#1(#2){%
 \begingroup\edef\x{\endgroup
   \unexpanded{\NAT@bare@aux#1}(\@firstofone#2)}\x}
\makeatother

This will swallow the offending part, while keeping the correct sorting order.

The problem is in what's presented to LaTeX and natbib to interpret. If you look at the .bbl file, you'll see that the entry starts with

\bibitem[Knuth({\noopsort{1973c}}1981)]{book-full}

Now natbib uses this to produce the results from \citep{book-full} (or \citet), by applying various commands. The relevant one (that I found with some grep search) is \NAT@bare that requires its arguments in the form

\def\NAT@bare#1(#2)#3(@)#4\@nil#5{%

The relevant part is #1(#2), which is derived from the argument to \bibitem in brackets; natbib wants to perform a numeric check, assuming that what's in parentheses is the year. But it finds {\noopsort{1973c}}1981 and this breaks off. So I make an alias to the original \NAT@bare command, calling it \NAT@bare@aux and do

\def\NAT@bare#1(#2){%
  \begingroup\edef\x{\endgroup
  \unexpanded{\NAT@bare@aux#1}(\@firstofone#2)}\x}

So, when natbib wants to execute \NAT@bare (it's so instructed by other macros), it will take the argument Knuth({\noopsort{1973c}1981}) and perform an\edef; thus\x` will be defined as the result of completely expanding

\endgroup\unexpanded{\NAT@bare@aux Knuth}(\@firstofone{\noopsort{1973c}}1981)

The job of \@firstofone is just to strip the braces, and the expansion of \noopsort{1973c} is just empty. So at the end LaTeX will see

\NAT@bare@aux Knuth(1981)

which is in the right format. The \endgroup balances the \begingroup and vanishes together with the definition of \x which has already done its job.

If the entry is normal, say \bibitem[Author(2012)] (by the way, Happy New Year), \x will be defined as the full expansion of

\endgroup\unexpanded{\NAT@bare@aux Author}(\@firstofone 2012)

and \@firstofone 2 will simply return 2, so in the end LaTeX will see

\NAT@bare@aux Author(2012)

as it should.