2

I am trying to generate an index of person names, which are used in a book.

On its first appearance, the person is introduced using it complete name (i.e. name and surname and maybe an additional explanation), later on in the text, only the persons name will be used.

I intended to use good old makeindex to generate the index, as there is no other index needed. Therefore I wrote macros, one for the initial appearance of the person, one for all further appearances. The initial appearance macro uses name and surname as mandatory arguments, the second macro uses only the name as mandatory argument.

I thought, it would be clever, to write the names via the \index-command using the @like this \index{#2@#2} (assuming, the persons name is always stored in #2, its surname in #3). So, for the initial appearance the index will look like this:

\index{#2@#2, #3}

And this is the call for further appearances:

\index{#2}

Unfortunately, Makeindex won't identify those as unique names. Even when I changed the second macro to use the @ also:

\index{#2@#2}

will show up as an second entry of the same name.

What went wrong, what must I change?

Here is the MWE:

\documentclass[a4paper]{scrartcl}
\usepackage[main=english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{makeidx}
\usepackage{ifthen}


%% Definition of macros
%% Initpers is used, when the person is first mentioned.  It takes 4
%% arguments:
%%   #1: Output in the text (optional)
%%   #2: Name (mandatory)
%%   #3: Surname (mandatory)
%%   #4: Description (mandatory but most of the time empty)
%% 
\newcommand{\initpers}[4][]{%
  % output the name and surname into the text.  If the optional
  % argument #1 is given, use this, else use the mandatory arguments
  % #2 and #3
  \ifthenelse{\equal{#1}{}}{
    {#3 #2}}{%
    #1}%
  % Now, insert Name, Surname and the Description (if given) into the index.
  \ifthenelse{\equal{#4}{}}{%
    \index{#2@#2, #3}}{%
    \index{#2@#2, #3 (#4)}}%
}%
%% This macro shall be used, whenever a person, which was already
%% introduced, shall be mentioned again.  In this case, only the name
%% is used.  
\newcommand{\pers}[1]{%
  % output the name into the text, enter it into the index.
  % #1\index{#1}
  #1\index{#1@#1}%
}%

% generate the index
\makeindex
\begin{document}
This is some text, in which I will reflect on the book ``The two
cultures'' by \initpers{Snow}{Charles Percy}{}.  This book \dots

%% For the sake of this example, lets assume, we mention Mr. Snow
%% later on
\newpage
This was also mentioned by \pers{Snow} as described earlier.

\printindex

\end{document}

This is the unfortunate result, because, it contains two entries of Mr. Snow, despite the fact, that this is one person and should be listed as Snow, Charles Percy, 1, 2:

enter image description here

  • With other words, the entry Snow, Charles Percy should appear only once although occuring twice (in your MWE)? Thinking about this, I feel you should use glossaries rather than the plain makeidx here. The @ has a special meaning for makeindex as a sorting indicator, which is possibly not what you want here – user31729 Mar 29 '18 at 8:45
  • @ChristianHupfer, the index shall list Snow, Charles Percy only once but listing two page numbers in this case. – Jan Mar 29 '18 at 8:54
2

My solution stores the names and the potential description into an expl3 property list and takes care of the index format. If there is no description, the ordinary format is extracted, otherwise the description is added in the \index - call.

This way, the Index is 'compressed'.

I also used \NewDocumentCommand to provide for an optional argument at the end of \InitPerson.

The macros \displayname, \descriptionformat and \nodescriptionformat are just wrappers in order to prevent the gobbling of white space in the \ExplSyntaxOn...\ExplSyntaxOff regime.

\documentclass[a4paper]{scrartcl}
\usepackage[main=english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{makeidx}

\usepackage{ifthen}

\usepackage{xparse}




%% Definition of macros
%% Initpers is used, when the person is first mentioned.  It takes 4
%% arguments:
%%   #1: Output in the text (optional)
%%   #2: Name (mandatory)
%%   #3: Surname (mandatory)
%%   #4: Description (mandatory but most of the time empty)
%% 
\newcommand{\initpersold}[4][]{%
  % output the name and surname into the text.  If the optional
  % argument #1 is given, use this, else use the mandatory arguments
  % #2 and #3
  \ifthenelse{\equal{#1}{}}{
    {#3 #2}}{%
    #1}%
  % Now, insert Name, Surname and the Description (if given) into the index.
  \ifthenelse{\equal{#4}{}}{%
    \index{#2@#2, #3}}{%
    \index{#2@#2, #3 (#4)}}%
}%
%% This macro shall be used, whenever a person, which was already
%% introduced, shall be mentioned again.  In this case, only the name
%% is used.  
\newcommand{\persold}[1]{%
  % output the name into the text, enter it into the index.
  % #1\index{#1}
  #1\index{#1@#1}%
}%

\newcommand{\displayname}[2]{%
  #1 #2%
}



\newcommand{\descriptionformat}[3]{%
  #1, #2 (#3)%
}

\newcommand{\nodescriptionformat}[2]{%
  #1, #2%
}

\ExplSyntaxOn

\newcommand{\propextractname}[1]{%
  \prop_item:Nn \g_jan_person_prop {#1}%
}

\newcommand{\propextractdescription}[1]{%
   \displayname{\prop_item:Nn \g_jan_person_prop {#1}}{(\prop_item:Nn \g_jan_person_prop {#1-description})}%
}


\prop_new:N \g_jan_person_prop
\seq_new:N \g_jan_personused_seq

\NewDocumentCommand{\InitPerson}{o+m+m+o}{%
  \IfNoValueTF{#1}{%
    \displayname{#3}{#2}%
  }{%
    #1%
  }%
  \IfValueTF{#4}{%
    \prop_gput:Nnn \g_jan_person_prop {#2-description} {#4}
    \index{\descriptionformat{#2}{#3}{#4}} 
  }{
    \index{\nodescriptionformat{#2}{#3}}
  }
  \prop_gput:Nnn \g_jan_person_prop {#2} {#3}
}

\NewDocumentCommand{\Person}{+m}{%
  #1%
  \prop_if_in:NnTF \g_jan_person_prop {#1} 
  {
    \prop_if_in:NnTF \g_jan_person_prop {#1-description}  
    {
      \typeout{Yes: #1}
      \index{#1,~\propextractdescription{#1}}
    }{
      \index{#1,~\propextractname{#1}}
    }
  }{
    \index{#1}
  }
}

\ExplSyntaxOff

\makeindex
\begin{document}

This is some text, in which I will reflect on the book ``The two
cultures'' by \InitPerson{Snow}{Charles Percy}.  This book \dots

\InitPerson{Vader}{Darth}[Father of Luke Skywalker]


%% For the sake of this example, lets assume, we mention Mr. Snow
%% later on
\clearpage
This was also mentioned by \Person{Snow} as described earlier.

The arch villain \Person{Vader}

\clearpage 
\Person{Snow} 

\printindex

\end{document}

enter image description here

2

The part before @ is only used for sorting purposes, but \index{a@aa} and \index{a@aabb} are different entries.

The code proposed by Christian Hupfer can be streamlined. You should perhaps rethink to what happens when the first optional argument is given to \initpers.

\documentclass[a4paper]{scrartcl}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[main=english]{babel}

\usepackage{makeidx} % or imakeidx
\usepackage{xparse}

\ExplSyntaxOn
%% Definition of macros
%% Initpers is used, when the person is first mentioned.  It takes 4
%% arguments:
%%   #1: Output in the text (optional)
%%   #2: Family name (mandatory)
%%   #3: Given name (mandatory)
%%   #4: Description (optional)
%% 
\NewDocumentCommand{\initpers}{ommo}
 {
  % output the name and surname into the text.  If the optional
  % argument #1 is given, use this, else use the mandatory arguments
  % #2 and #3
  \IfNoValueTF { #1 }
   {
    % no optional argument, print given name and family name
    #3~#2
   }
   {
    % optional argument, print just it
    #1
   }
  % store the data
  \prop_gput:Nnn \g_jan_persons_prop { #2 familyname } { #2 } 
  \prop_gput:Nnn \g_jan_persons_prop { #2 givenname } { #3 } 
  \IfValueT { #4 }
   {
    \prop_gput:Nnn \g_jan_persons_prop { #2 description } { #4 }
   }
  \index{#2@\usename{#2}}
 }

\NewDocumentCommand{\pers}{m}
 {
  % output the name into the text, enter it into the index.
  % #1\index{#1}
  #1\index{#1@\usename{#1}}
 }

\NewDocumentCommand{\usename}{m}
 {
  \prop_item:Nn \g_jan_persons_prop { #1 familyname }
  ,\c_space_tl
  \prop_item:Nn \g_jan_persons_prop { #1 givenname }
  \prop_if_in:NnTF \g_jan_persons_prop { #1 description }
   {
    \c_space_tl (\prop_item:Nn \g_jan_persons_prop { #1 description })
   }
 }
\prop_new:N \g_jan_persons_prop

\ExplSyntaxOff
% generate the index
\makeindex

\begin{document}
This is some text, in which I will reflect on the book ``The two
cultures'' by \initpers{Snow}{Charles Percy}.  This book \dots

We also talk about \initpers{Vader}{Darth}[Father of Luke Skywalker]
and \initpers[Stendhal]{Beyle}{Henri}

%% For the sake of this example, lets assume, we mention Mr. Snow
%% later on
\clearpage
This was also mentioned by \pers{Snow} as described earlier

And we also have \pers{Vader} and \pers{Beyle}.

\printindex

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.