Как дедуплицировать и отсортировать список \pagerefs?

Как дедуплицировать и отсортировать список \pagerefs?

У меня есть документ, состоящий из трех больших многостраничных таблиц, все из которых имеют ссылки на элементы в списке рисунков в конце. Генерируя метки md5 при генерации latex для таблиц, я могу включить ссылки обратно на исходные таблицы. Однако несколько элементов, ссылающихся на рисунок, часто будут находиться на одной странице, что приведет к таким чудовищностям:

Figure 31 - This figure was referenced on pages 4, 4, 4, 14, 17, 17, 27, 30, 30, 6, 6, 6, 6, 6, 6, 8, 8, 8, 19, 19, 20, 20, 21, 21, 21, 21, 21, 32, 32, 33, 33, 34, 34, 34 34, and 34.

В сгенерированном документе каждый из этих номеров страниц является ссылкой на строку в таблице, которая ссылается на этот рисунок.

Я хотел бы добиться двух вещей:

  1. Устранить дубликаты, чтобы на данной странице была только ссылка на первую запись таблицы.
  2. (необязательно) Сортировать список по номеру страницы

Я видел вопрос о дедупликации списказдесь, но я новичок в латексе и даже мой минимальный рабочий пример, размещенный ниже, не может работать:

\documentclass[letterpaper,11pt,openany,oneside]{book}

\makeatletter
\def\removeduplicates#1#2{\begingroup
  \let\@tempa#1%
  \def\@tempb{}%
  \@for\next:=\@tempa\do
    {\@ifundefined{lstel@\next}
      {\edef\@tempb{\@tempb,\next}
       \expandafter\let\csname lstel@\next\endcsname\@empty}
      {}%
    }%
  \edef\x{\endgroup\def\noexpand#2{\@tempb}}\x
  \expandafter\strip@comma#2\@nil#2}
\def\strip@comma,#1\@nil#2{\def#2{#1}}
\makeatother

\begin{document}
% some labels on page 1
\section{John} \label{john}
john doe
\section{Mary} \label{mary}
mary sue

%move to page 2
\clearpage
\section{George} \label{george}
george weasley
\section{Australia} \label{australia}
crocodile dundee

%new page, where the references are
\clearpage
\section{References}

\def\alist{\pageref{john},\pageref{mary},\pageref{george},\pageref{australia},\pageref{australia},\pageref{john}}   
\removeduplicates\alist\blist

\show\blist
\show\alist

\removeduplicates\alist\alist
\show\alist

\end{document}

Когда я пытаюсь запустить это, простую модификацию примера по ссылке, я получаю около дюжины ошибок для каждой из строк \renewduplicates, жалуясь на missing \endcsname, extra \endcsname, и extra \else. Я понятия не имею, как это отладить.

решение1

Вы используете \edefи \pagerefне можете использовать его в этом контексте, а также не можете появляться в\csname...\endcsname

Избежание полного расширения будет работать вместе с преобразованием расширения первого уровня в строку, \nextкогда в \csname...\endcsname.

\providecommand{\expandonce}{\unexpanded\expandafter}
\makeatletter
\def\removeduplicates#1#2{\begingroup
  \let\@tempa#1%
  \def\@tempb{}%
  \@for\next:=\@tempa\do
    {\@ifundefined{lstel@\detokenize\expandafter{\next}}
      {\edef\@tempb{\expandonce{\@tempb},\expandonce{\next}}
       \expandafter\let\csname lstel@\detokenize\expandafter{\next}\endcsname\@empty}
      {}%
    }%
  \edef\x{\endgroup\def\noexpand#2{\expandonce{\@tempb}}}\x
  \expandafter\strip@comma#2\@nil#2}
\def\strip@comma,#1\@nil#2{\def#2{#1}}
\makeatother

Однако есть более хитрый способ:

\documentclass[letterpaper,11pt,openany,oneside]{book}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\removeduplicates}{mm}
 {
  \seq_set_split:NnV \l_tmpa_seq { , } #1
  \seq_remove_duplicates:N \l_tmpa_seq
  \tl_set:Nx #2 { \seq_use:Nn \l_tmpa_seq { , } }
 }
\ExplSyntaxOff

\begin{document}
% some labels on page 1
\section{John} \label{john}
john doe
\section{Mary} \label{mary}
mary sue

%move to page 2
\clearpage
\section{George} \label{george}
george weasley
\section{Australia} \label{australia}
crocodile dundee

%new page, where the references are
\clearpage
\section{References}

\def\alist{\pageref{john},\pageref{mary},\pageref{george},%
  \pageref{australia},\pageref{australia},\pageref{john}}   

\removeduplicates\alist\blist

\show\blist
\show\alist

\removeduplicates\alist\alist
\show\alist

\end{document}

Вот вывод на моем терминале:

> \blist=macro:
->\pageref {john},\pageref {mary},\pageref {george},\pageref {australia}.
l.37 \show\blist

? 
> \alist=macro:
->\pageref {john},\pageref {mary},\pageref {george},\pageref {australia},\pager
ef {australia},\pageref {john}.
l.38 \show\alist

? 
> \alist=macro:
->\pageref {john},\pageref {mary},\pageref {george},\pageref {australia}.
l.41 \show\alist

? 

На этом уровне сортировка невозможна.


Если вы хотите удалить дубликаты номеров страниц, а не сами \pageref{...}дубликаты, вам нужна расширяемая версия, \pagerefпредоставленная refcount. Я покажу код в двух версиях. Первая expl3:

\documentclass[letterpaper,11pt,openany,oneside]{book}
\usepackage{refcount}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\removeduplicates}{mm}
 {
  \clist_set:Nx \l_tmpa_clist { #1 }
  \clist_remove_duplicates:N \l_tmpa_clist
  \tl_set:NV #2 \l_tmpa_clist
 }
\ExplSyntaxOff

\begin{document}
% some labels on page 1
\section{John} \label{john}
john doe
\section{Mary} \label{mary}
mary sue

%move to page 2
\clearpage
\section{George} \label{george}
george weasley
\section{Australia} \label{australia}
crocodile dundee

%new page, where the references are
\clearpage
\section{References}

\def\alist{\getpagerefnumber{john},\getpagerefnumber{mary},\getpagerefnumber{george},%
  \getpagerefnumber{australia},\getpagerefnumber{australia},\getpagerefnumber{john}}   

\removeduplicates\alist\blist

\show\blist
\show\alist

\removeduplicates\alist\alist
\show\alist

\alist

\blist

\end{document}

А вот «классическая» версия:

\documentclass[letterpaper,11pt,openany,oneside]{book}
\usepackage{refcount}

\makeatletter
\def\removeduplicates#1#2{\begingroup
  \let\@tempa#1%
  \def\@tempb{}%
  \@for\next:=\@tempa\do
    {\@ifundefined{lstel@\next}
      {\edef\@tempb{\@tempb,\next}%
       \expandafter\let\csname lstel@\next\endcsname\@empty}
      {}%
    }%
  \edef\x{\endgroup\def\noexpand#2{\@tempb}}\x
  \expandafter\strip@comma#2\@nil#2}
\def\strip@comma,#1\@nil#2{\def#2{#1}}
\makeatother

\begin{document}
% some labels on page 1
\section{John} \label{john}
john doe
\section{Mary} \label{mary}
mary sue

%move to page 2
\clearpage
\section{George} \label{george}
george weasley
\section{Australia} \label{australia}
crocodile dundee

%new page, where the references are
\clearpage
\section{References}

\def\alist{\getpagerefnumber{john},\getpagerefnumber{mary},\getpagerefnumber{george},\getpagerefnumber{australia},\getpagerefnumber{australia},\getpagerefnumber{john}}   
\removeduplicates\alist\blist

\show\blist
\show\alist

\removeduplicates\alist\alist
\show\alist

\blist

\alist

\end{document}

Обе версии определяют \blistи переопределяют \alist, чтобы содержать «1,2».


Версия, использующая построенный список для печати гиперссылок на страницы.

\documentclass[letterpaper,11pt,openany,oneside]{book}
\usepackage{refcount}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewDocumentCommand{\createlinks}{m}
 {
  \seq_set_split:Nnx \l_tmpa_seq { , } { #1 }
  \seq_remove_duplicates:N \l_tmpa_seq
  \seq_clear:N \l_tmpb_seq
  \seq_map_inline:Nn \l_tmpa_seq
   {
    \seq_put_right:Nn \l_tmpb_seq { \hyperlink { page.##1 } { ##1 } }
   }
  \seq_use:Nn \l_tmpb_seq { ,~ }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx }
\ExplSyntaxOff

\begin{document}
% some labels on page 1
\section{John} \label{john}
john doe
\section{Mary} \label{mary}
mary sue

%move to page 2
\clearpage
\section{George} \label{george}
george weasley
\section{Australia} \label{australia}
crocodile dundee

%new page, where the references are
\clearpage
\section{References}

\def\alist{\getpagerefnumber{john},\getpagerefnumber{mary},\getpagerefnumber{george},%
  \getpagerefnumber{australia},\getpagerefnumber{australia},\getpagerefnumber{john}}   

\createlinks\alist

\end{document}

Связанный контент