[Tex/LaTex] Biblatex/Biber replace author name with dash for each author

biberbiblatex

In the bibliography of authoryear-type bibliography styles, BibLaTeX can replace repeated author lists (with all authors the same) with a dash or similar. Is is possible to change this behaviour so that the replacement is made on a per-author basis? This is found in such journals as Palaeontology.

As an example, the following references:

AUTHOR, A. 2000. First reference. Journal A, 2, 21–22.
AUTHOR, A. & BAUTHOR, B. 2001. Second reference. Journal B, 4, 34–46.
AUTHOR, A., BAUTHOR, B. & CAUTHOR, C. 2002. Third reference. Journal C, 6, 89–99.

Would be published as:

AUTHOR, A. 2000. First reference. Journal A, 2, 21–22.
——— & BAUTHOR, B. 2001. Second reference. Journal B, 4, 34–46.
——— ——— & CAUTHOR, C. 2002. Third reference. Journal C, 6, 89–99.

With three em dashes replacing each repeated author.

A reply to With biblatex, is it possible to replace one author of an author list by a bibnamedash or idem string? describes commands to replace a repeated first author only with Idem/Eadem/Eidem/Eaedem, but can this be applied to several authors in the author list?

Addendum:
This image is from Porro, L.B., et al., "Computed Tomography, …",
Palaeontology, 2015, p.16.

bibliography example with multiple 3-em dashes

Best Answer

The general idea is inspired by Audrey's answer to With biblatex, is it possible to replace one author of an author list by a bibnamedash or idem string?, but takes a very different approach in the end.

You can control the inserted dash with \bibnamedash, the separator between two of those dashes is \multidashdelim, the last is separated by \finaldashdelim.

\makeatletter
\renewcommand*{\bibnamedash}{\textemdash\textemdash\textemdash}
\newcommand*{\multidashdelim}{\addspace}
\newcommand*{\finaldashdelim}{\finalnamedelim}

\newtoggle{bbx:goahead}
\InitializeBibliographyStyle{\global\csundef{bbx@lastname@1}}

\newbibmacro*{bbx:printlist}[1]{%
  \toggletrue{bbx:goahead}%
  \printnames[dashed:given-family]{#1}}

\newbibmacro*{dash:delim}{%
  \ifnumgreater{\value{listcount}}{\value{liststart}}
    {\ifboolexpr{
       test {\ifnumless{\value{listcount}}{\value{liststop}}}
       or
       test \ifmorenames
     }
       {\multidashdelim}
       {\finaldashdelim}}
    {}}

\DeclareNameFormat{dashed:given-family}{%
   \ifboolexpr{togl {bbx:goahead} 
               and test {\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}}}
     {\usebibmacro{dash:delim}\bibnamedash}
     {\global\togglefalse{bbx:goahead}%
      \ifgiveninits
        {\usebibmacro{name:family-given}
           {\namepartfamily}%
           {\namepartgiveni}%
           {\namepartprefix}%
           {\namepartsuffix}}
        {\usebibmacro{name:family-given}
          {\namepartfamily}%
          {\namepartgiven}%
          {\namepartprefix}%
          {\namepartsuffix}}}%
  \csxdef{bbx@lastname@\the\value{listcount}}{\thefield{hash}}}

\renewbibmacro*{author}{%
  \ifboolexpr{ test {\ifuseauthor}
               and not test {\ifnameundef{author}} }
    {\usebibmacro{bbx:printlist}{author}%
     \setunit{\addcomma\space}%
     \usebibmacro{authorstrg}}
    {\global\csundef{bbx@lastname@1}}}

\renewbibmacro*{bbx:editor}[1]{%
  \ifboolexpr{ test {\ifuseeditor}
               and not test {\ifnameundef{editor}} }
    {\usebibmacro{bbx:printlist}{editor}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{editor}}
    {\global\csundef{bbx@lastname@1}}}

\renewbibmacro*{bbx:translator}[1]{%
  \ifboolexpr{ test {\ifusetranslator}
               and not test {\ifnameundef{translator}} }
    {\usebibmacro{bbx:printlist}{translator}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{translator}}
    {\global\csundef{bbx@lastname@1}}}
\makeatother

If you want the dash to be able to jump over names, you can just change the line

\ifboolexpr{togl {bbx:goahead} 
            and test {\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}}}

to

\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}

you can then also remove all mentions of bbx:goahead.

MWE

\documentclass{scrartcl}
\usepackage[T1]{fontenc}
\usepackage[american]{babel}
\usepackage{csquotes}
\usepackage[style=authortitle,
            maxbibnames=999,
            backend=biber,
            sorting=nyt,
            abbreviate=false]{biblatex}

\makeatletter
\renewcommand*{\bibnamedash}{\textemdash\textemdash\textemdash}
\newcommand*{\multidashdelim}{\addspace}
\newcommand*{\finaldashdelim}{\finalnamedelim}

\newtoggle{bbx:goahead}
\InitializeBibliographyStyle{\global\csundef{bbx@lastname@1}}

\newbibmacro*{bbx:printlist}[1]{%
  \toggletrue{bbx:goahead}%
  \printnames[dashed:given-family]{#1}}

\newbibmacro*{dash:delim}{%
  \ifnumgreater{\value{listcount}}{\value{liststart}}
    {\ifboolexpr{
       test {\ifnumless{\value{listcount}}{\value{liststop}}}
       or
       test \ifmorenames
     }
       {\multidashdelim}
       {\finaldashdelim}}
    {}}

\DeclareNameFormat{dashed:given-family}{%
   \ifboolexpr{togl {bbx:goahead} 
               and test {\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}}}
     {\usebibmacro{dash:delim}\bibnamedash}
     {\global\togglefalse{bbx:goahead}%
      \ifgiveninits
        {\usebibmacro{name:family-given}
           {\namepartfamily}%
           {\namepartgiveni}%
           {\namepartprefix}%
           {\namepartsuffix}}
        {\usebibmacro{name:family-given}
          {\namepartfamily}%
          {\namepartgiven}%
          {\namepartprefix}%
          {\namepartsuffix}}}%
  \csxdef{bbx@lastname@\the\value{listcount}}{\thefield{hash}}}

\renewbibmacro*{author}{%
  \ifboolexpr{ test {\ifuseauthor}
               and not test {\ifnameundef{author}} }
    {\usebibmacro{bbx:printlist}{author}%
     \setunit{\addcomma\space}%
     \usebibmacro{authorstrg}}
    {\global\csundef{bbx@lastname@1}}}

\renewbibmacro*{bbx:editor}[1]{%
  \ifboolexpr{ test {\ifuseeditor}
               and not test {\ifnameundef{editor}} }
    {\usebibmacro{bbx:printlist}{editor}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{editor}}
    {\global\csundef{bbx@lastname@1}}}

\renewbibmacro*{bbx:translator}[1]{%
  \ifboolexpr{ test {\ifusetranslator}
               and not test {\ifnameundef{translator}} }
    {\usebibmacro{bbx:printlist}{translator}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{translator}}
    {\global\csundef{bbx@lastname@1}}}
\makeatother

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@BOOK{bookABC,
  author = {Anne Uthor and Enid Blyton and William Riter},
  title = {UBR},
  date = {1990},}
@BOOK{bookADC,
  author = {Anne Uthor and James Joyce and William Riter},
  title = {UJR},
  date = {1991},}
@BOOK{book1,
  author = {Buchautor, Emma-Louise},
  gender = {sf},
  title = {First Title},
  date = {2001}}
@BOOK{book2,
  author = {Buchautor, Emma-Louise},
  title = {Same female author},
  date = {2002}}
@BOOK{book3,
  author = {Buchautor, Emma-Louise and van Helsing, Sarah},
  gender = {pf},
  title = {Same first author whose gender is unspecified},
  date = {2003}}
@BOOK{book4,
  author = {Buchautor, Emma-Louise and van Helsing, Sarah and Knuth, Donald E.},
  gender = {pp},
  title = {Same first and second female authors},
  date = {2004}}
@BOOK{book5,
  editor = {Buchautor, Emma-Louise and van Helsing, Sarah and Knuth, Donald E. and Cicero, Marcus Tullius},
  gender = {pp},
  title = {Same leading names of mixed genders},
  date = {2005}}
@BOOK{book6,
  editor = {van Helsing, Abraham},
  title = {Different name},
  date = {2006}}
@BOOK{book7,
  author = {van Helsing, Abraham},
  gender = {sm},
  translator = {van Helsing, Abraham},
  title = {Same male name},
  date = {2007}}
@BOOK{book8,
  editor = {van Helsing, Abraham and Jackson, Tom},
  gender = {pm},
  translator = {Cicero, Marcus Tullius},
  title = {Same first male author},
  date = {2008}}
@BOOK{book9,
  author = {van Helsing, Abraham and Jackson, Tom},
  title = {Same male names},
  date = {2009}}
@BOOK{book10,
  author = {van Helsing, Abraham and Jackson, Tom},
  gender = {pm},
  title = {Same authors of unspecified gender},
  date = {2010}}
@article {JMOR:JMOR10504,
  author = {Markey, Molly J. and Marshall, Charles R.},
  title = {Linking form and function of the fibrous joints in the skull: A new quantification scheme for cranial sutures using the extant fish Polypterus endlicherii},
  journal = {Journal of Morphology},
  volume = {268},
  number = {1},
  doi = {10.1002/jmor.10504},
  pages = {89--102},
  date = {2007-01},
}
@article {markey:pnas,
  author = {Markey, Molly J. and Marshall, Charles R.},
  title = {Terrestrial-style feeding in a very early aquatic tetrapod is supported by evidence from experimental analysis of suture morphology},
  journal = {Proceedings of the National Academy of Sciences},
  volume = {104},
  number = {17},
  doi = {10.1073/pnas.0701706104},
  pages = {7134-7138},
  date = {2007-04},
}
@article {Markey2085,
  author = {Markey, Molly J. and Main, Russell P. and Marshall, Charles R.},
  title = {\emph{In vivo} cranial suture function and suture morphology in the extant fish \emph{Polypterus}: implications for inferring skull function in living and fossil fish},
  volume = {209},
  number = {11},
  pages = {2085--2102},
  year = {2006},
  doi = {10.1242/jeb.02266},
  journal = {Journal of Experimental Biology},
}
\end{filecontents}
\addbibresource{\jobname.bib}

\begin{document}
\nocite{*}
\printbibliography
\end{document}

example output


We can make this a bit more safe if we use

\newcounter{bbx:currnames}
\newcounter{bbx:lastnames}

\newbibmacro*{bbx:printlist}[1]{%
  \setcounter{bbx:currnames}{0}%
  \toggletrue{bbx:goahead}%
  \printnames[dashed:given-family]{#1}%
  \setcounter{bbx:lastnames}{\value{bbx:currnames}}}

a new macro dash:cleanup that can be implemented as

\newbibmacro{dash:cleanup}{\setcounter{bbx:lastnames}{0}}

and is used as

\InitializeBibliographyStyle{\usebibmacro{dash:cleanup}}

\renewbibmacro*{author}{%
  \ifboolexpr{ test {\ifuseauthor}
               and not test {\ifnameundef{author}} }
    {\usebibmacro{bbx:printlist}{author}%
     \setunit{\addcomma\space}%
     \usebibmacro{authorstrg}}
    {\usebibmacro{dash:cleanup}}}

\renewbibmacro*{bbx:editor}[1]{%
  \ifboolexpr{ test {\ifuseeditor}
               and not test {\ifnameundef{editor}} }
    {\usebibmacro{bbx:printlist}{editor}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{editor}}
    {\usebibmacro{dash:cleanup}}}

\renewbibmacro*{bbx:translator}[1]{%
  \ifboolexpr{ test {\ifusetranslator}
               and not test {\ifnameundef{translator}} }
    {\usebibmacro{bbx:printlist}{translator}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{translator}}
    {\usebibmacro{dash:cleanup}}}

You will then have to change the

\ifboolexpr{togl {bbx:goahead} 
            and test {\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}}}

in dashed:given-family to the slightly longer

\ifboolexpr{togl {bbx:goahead} 
            and not test {\ifnumgreater{\value{listcount}}{\value{bbx:lastnames}}}
            and test {\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}}}

Another clean-up solution is

\makeatletter
\newtoggle{bbx:goahead}
\newcounter{bbx:currnames}
\newcounter{bbx:lastnames}

\renewcommand*{\bibnamedash}{\textemdash\textemdash\textemdash}
\newcommand*{\multidashdelim}{\addspace}
\newcommand*{\finaldashdelim}{\finalnamedelim}

\InitializeBibliographyStyle{\usebibmacro{dash:cleanup}{0}}

\newbibmacro*{bbx:printlist}[1]{%
  \setcounter{bbx:currnames}{0}%
  \toggletrue{bbx:goahead}%
  \printnames[dashed:given-family]{#1}%
  \usebibmacro{dash:cleanup}{\value{bbx:currnames}+1}%
  \setcounter{bbx:lastnames}{\value{bbx:currnames}}}

\newbibmacro*{dash:delim}{%
  \ifnumgreater{\value{listcount}}{\value{liststart}}
    {\ifboolexpr{
       test {\ifnumless{\value{listcount}}{\value{liststop}}}
       or
       test \ifmorenames
     }
       {\multidashdelim}
       {\finaldashdelim}}
    {}}

\DeclareNameFormat{dashed:given-family}{%
   \addtocounter{bbx:currnames}{1}%
   \ifboolexpr{togl {bbx:goahead} and not test \iffirstonpage
               and test {\iffieldequalcs{hash}{bbx@lastname@\the\value{listcount}}}}
     {\usebibmacro{dash:delim}\bibnamedash}
     {\global\togglefalse{bbx:goahead}%
      \ifgiveninits
        {\usebibmacro{name:family-given}
           {\namepartfamily}%
           {\namepartgiveni}%
           {\namepartprefix}%
           {\namepartsuffix}}
        {\usebibmacro{name:family-given}
          {\namepartfamily}%
          {\namepartgiven}%
          {\namepartprefix}%
          {\namepartsuffix}}}%
  \csxdef{bbx@lastname@\the\value{listcount}}{\thefield{hash}}}

\newbibmacro{dash:cleanup}[1]{%
  \ifnumgreater{\value{bbx:lastnames}}{#1}
    {\global\csundef{bbx@lastname@\the\value{bbx:lastnames}}%
     \addtocounter{bbx:lastnames}{-1}%
     \usebibmacro{dash:cleanup}{#1}}
    {}}

\renewbibmacro*{author}{%
  \ifboolexpr{ test {\ifuseauthor}
               and not test {\ifnameundef{author}} }
    {\usebibmacro{bbx:printlist}{author}%
     \setunit{\addcomma\space}%
     \usebibmacro{authorstrg}}
    {\usebibmacro{dash:cleanup}{0}}}

\renewbibmacro*{bbx:editor}[1]{%
  \ifboolexpr{ test {\ifuseeditor}
               and not test {\ifnameundef{editor}} }
    {\usebibmacro{bbx:printlist}{editor}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{editor}}
    {\usebibmacro{dash:cleanup}{0}}}

\renewbibmacro*{bbx:translator}[1]{%
  \ifboolexpr{ test {\ifusetranslator}
               and not test {\ifnameundef{translator}} }
    {\usebibmacro{bbx:printlist}{translator}%
     \setunit{\addcomma\space}%
     \usebibmacro{#1}%
     \clearname{translator}}
    {\usebibmacro{dash:cleanup}{0}}}
\makeatother

In any case you could want to add a and not test \iffirstonpage to the \ifboolexpr in dashed:given-family.