2

In my LaTeX-file I create a variable \authors which could have content:

[Doe] John Doe; [Potter] Harry Potter; [Clinton] Bill Clinton; [Obama] Barack Obama; ...

This is dynamically created, so I could vary it a bit (I've got a data-file from which I retreive the first and last names). Is there a way I could turn this variable-text into the ordered (by last name) and neatly-displayed list

Bill Clinton, John Doe, Barack Obama, Harry Potter,...

1) is this possible, 2) should I store this initial information in a variable or should I store them in a file or something else?

konewka
  • 377

2 Answers2

3

Here is an implementation using expl3 and the module l3sort:

\documentclass{article}

\usepackage{xparse,l3sort,pdftexcmds}

\ExplSyntaxOn

\cs_set_eq:Nc \konewka_strcmp:nn { pdf@strcmp }

\NewDocumentCommand{\addauthor}{ o m m }
 {
  \IfNoValueTF{#1}
   {
    \konewka_add_author:nnn { #3 } { #2 } { #3 }
   }
   {
    \konewka_add_author:nnn { #1 } { #2 } { #3 }
   }
 }

\NewDocumentCommand{\printauthors}{ }
 {
  \konewka_print_authors:
 }

\seq_new:N \g_konewka_authors_id_seq
\seq_new:N \l__konewka_authors_full_seq

\cs_new_protected:Npn \konewka_add_author:nnn #1 #2 #3
 {
  \seq_gput_right:Nn \g_konewka_authors_id_seq { #1 }
  \prop_new:c { g_konewka_author_#1_prop }
  \prop_gput:cnn { g_konewka_author_#1_prop } { fname } { #2 }
  \prop_gput:cnn { g_konewka_author_#1_prop } { lname } { #3 }
 }

\cs_new_protected:Npn \konewka_print_authors:
 {
  \seq_gsort:Nn \g_konewka_authors_id_seq
   {
    \string_compare:nnnTF {##1} {>} {##2} {\sort_reversed:} {\sort_ordered:}
   }
  \seq_clear:N \l__konewka_authors_full_seq
  \seq_map_inline:Nn \g_konewka_authors_id_seq
   {
    \seq_put_right:Nx \l__konewka_authors_full_seq
     {
      \prop_item:cn { g_konewka_author_##1_prop } { fname }
      \c_space_tl
      \prop_item:cn { g_konewka_author_##1_prop } { lname }
     }
   }
  \seq_use:Nn \l__konewka_authors_full_seq { ,~ }
 }

\prg_new_conditional:Npnn \string_compare:nnn #1 #2 #3 {TF}
  {
   \if_int_compare:w \konewka_strcmp:nn {#1}{#3} #2 \c_zero
    \prg_return_true:
   \else:
    \prg_return_false:
   \fi
  }

\ExplSyntaxOff

\begin{document}

\addauthor{John}{Doe}
\addauthor{Harry}{Potter}
\addauthor[Uthor]{Archibald}{\"Uthor}
\addauthor{Bill}{Clinton}
\addauthor{Barack}{Obama}

\printauthors

\end{document}

An author is added with \addauthor{<first name(s)>}{<last name>}; an optional argument is allowed for coping with special characters; this optional argument will be used both for indexing the property lists and for sorting.

enter image description here

See Sort subsections alphabetically for another application of \seq_sort:Nn

If you have two authors with the same last name, use the optional argument; for example

\addauthor[Doe@Jane]{Jane}{Doe}
\addauthor[Doe@John]{John}{Doe}

Note that the optional argument determines the sorting order; using @, which comes before letters in ASCII, will guarantee that the two Doe authors will be sorted before “Doeb”. One might use the same idea for all authors, that is, using “lname@fname” as sorting key, but this would give problems with special characters in the first name.

Here's a version that doesn't add an author if the sorting key (last name or optional argument) is already present in the database.

\documentclass{article}

\usepackage{xparse,l3sort,pdftexcmds}

\ExplSyntaxOn

\cs_set_eq:Nc \konewka_strcmp:nn { pdf@strcmp }

\NewDocumentCommand{\addauthor}{ o m m }
 {
  \IfNoValueTF{#1}
   {
    \konewka_add_author:nnn { #3 } { #2 } { #3 }
   }
   {
    \konewka_add_author:nnn { #1 } { #2 } { #3 }
   }
 }

\NewDocumentCommand{\printauthors}{ }
 {
  \konewka_print_authors:
 }

\seq_new:N \g_konewka_authors_id_seq
\seq_new:N \l__konewka_authors_full_seq

\msg_new:nnn { konewka/authors } { author~exists }
 {
  The ~ author ~ #1 ~ already ~ exists; ~ it ~ won't ~ be ~ added ~ again
 }

\cs_new_protected:Npn \konewka_add_author:nnn #1 #2 #3
 {
  \prop_if_exist:cTF { g_konewka_author_#1_prop }
   {
    \msg_warning:nnn { konewka/authors } { author~exists } { #1 }
   }
   {
    \seq_gput_right:Nn \g_konewka_authors_id_seq { #1 }
    \prop_new:c { g_konewka_author_#1_prop }
    \prop_gput:cnn { g_konewka_author_#1_prop } { fname } { #2 }
    \prop_gput:cnn { g_konewka_author_#1_prop } { lname } { #3 }
   }
 }

\cs_new_protected:Npn \konewka_print_authors:
 {
  \seq_gsort:Nn \g_konewka_authors_id_seq
   {
    \string_compare:nnnTF {##1} {>} {##2} {\sort_reversed:} {\sort_ordered:}
   }
  \seq_clear:N \l__konewka_authors_full_seq
  \seq_map_inline:Nn \g_konewka_authors_id_seq
   {
    \seq_put_right:Nx \l__konewka_authors_full_seq
     {
      \prop_item:cn { g_konewka_author_##1_prop } { fname }
      \c_space_tl
      \prop_item:cn { g_konewka_author_##1_prop } { lname }
     }
   }
  \seq_use:Nn \l__konewka_authors_full_seq { ,~ }
 }

\prg_new_conditional:Npnn \string_compare:nnn #1 #2 #3 {TF}
  {
   \if_int_compare:w \konewka_strcmp:nn {#1}{#3} #2 \c_zero
    \prg_return_true:
   \else:
    \prg_return_false:
   \fi
  }

\ExplSyntaxOff

\begin{document}

\addauthor{John}{Doe}
\addauthor{Harry}{Potter}
\addauthor[Uthor]{Archibald}{\"Uthor}
\addauthor{John}{Doe}
\addauthor{Bill}{Clinton}
\addauthor{Barack}{Obama}

\printauthors

\end{document}

Running this file will produce a warning such as

*************************************************
* konewka/authors warning: "author exists"
* 
* The author Doe already exists; it won't be added again
*************************************************

Change \msg_warning:nnn into \msg_error:nnn if you prefer an error is raised rather than a warning is issued.

egreg
  • 1,121,712
  • Awesome, this works great! – konewka Dec 11 '14 at 22:49
  • I'm now compiling the code on another computer (at my university) and I'm getting the error ! Undefined control sequence. <argument> \prop_item:cn {g_konewka_author_Clinton_prop}{fname}.... 1.74 \printauthors and several others (two for each author, both after \prop_item:cn) Have you got any idea where this can come from? – konewka Dec 12 '14 at 09:31
  • @konewka Outdated version of expl3 – egreg Dec 12 '14 at 09:47
  • Changing \prop_item:cn to \prop_get:cn solved the problem as well, and the script had a hard time dealing with two people with the same last name, so I changed all the prop-names from #1_prop to #1_#2_prop – konewka Dec 12 '14 at 09:52
  • @konewka The optional argument would help, wouldn't it? – egreg Dec 12 '14 at 10:01
  • Oh that works as well. Now I'm only wondering if (and how) it's possible to only add an author the first time it occurs (so if someone writes two articles, and I run \addauthor{John}{Doe} twice, it only adds it the first time. I tried to make something with \prop_if_exist:NTF, but couldn't get the right syntax – konewka Dec 12 '14 at 10:12
  • 1
    I added some notes about sorting and also a version that doesn't add the same author twice. – egreg Dec 12 '14 at 10:28
2

Just for illustration I show how to solve this task in plain TeX using OPmac macro where mergesort is implemented.

\input opmac

\def\sort{\begingroup\setprimarysorting\def\iilist{}\sortA}
\def\sortA#1#2{\ifx\relax#1\sortB\else
  \expandafter\addto\expandafter\iilist\csname,#1\endcsname
  \expandafter\preparesorting\csname,#1\endcsname
  \expandafter\edef\csname,#1\endcsname{{\tmpb}{#2}}%
  \expandafter\sortA\fi
}
\def\sortB{\def\message##1{}\dosorting
  \def\act##1{\ifx##1\relax\else \seconddata##1\sortC \expandafter\act\fi}%
  \gdef\tmpb{}\expandafter\act\iilist\relax
  \endgroup
}
\def\sortC#1&{\global\addto\tmpb{{#1}}}

\def\printauthors{\def\tmp{}\expandafter\printauthorsA\authors [] {} {}; }
\def\printauthorsA [#1] #2 #3; {%
   \ifx^#1^\expandafter\sort\tmp\relax\relax
           \def\tmp{}\expandafter\printauthorsB\tmpb\relax  
   \else\addto\tmp{{#1}{#2 #3}}\expandafter\printauthorsA\fi
}
\def\printauthorsB#1{\ifx\relax#1\else \tmp\def\tmp{, }#1\expandafter\printauthorsB\fi}

\def\authors{[Doe] John Doe; [Potter] Harry Potter; [Clinton] Bill Clinton;
             [Uthor] Archibald \"Uthor; [Obama] Barack Obama; }

Authors: \printauthors

\bye

The sorting is done by the data enclosed in [brackets] but the data outside [brackets] are printed. The multilingual support of sorting is possible by OPmac.

wipet
  • 74,238