这是该主题的延续: 按字母顺序显示 itemize 中的项目
但遗憾的是我没有足够的声誉来在那里发表评论。
@Werner 提出的方法非常完美,即使您需要在列表中显示格式化的文本。但我还有一个问题:我需要对包含格式的项目进行排序,并且需要能够编辑这些项目的文本和格式。因此我使用宏,所以我不能将这个技巧与包含纯文本的可选项一起使用。
以下是 MWE:
\documentclass{article}
\usepackage{datatool}% http://ctan.org/pkg/datatool
\newcommand{\sortitem}[1]{%
\DTLnewrow{list}% Create a new entry
\DTLnewdbentry{list}{description}{#1}% Add entry as description
}
\newenvironment{sortedlist}{%
\DTLifdbexists{list}{\DTLcleardb{list}}{\DTLnewdb{list}}% Create new/discard old list
}{%
\DTLsort{description}{list}% Sort list
\begin{itemize}%
\DTLforeach*{list}{\theDesc=description}{%
\item \theDesc}% Print each item
\end{itemize}%
}
\begin{document}
% I need to be able to edit these items and their formatting without changing the rest of my code
\newcommand{\firstitem}{ISDYNSTP: Is dynamic time step used ?}
\newcommand{\seconditem}{\textit{ISCDCA:}}
\newcommand{\thirditem}{\textbf{MVAR}}
\newcommand{\fourthitem}{IS2TL}
Default:
\begin{itemize}
\item \firstitem
\item \seconditem
\item \thirditem
\item \fourthitem
\end{itemize}
Sorted:
\begin{sortedlist}
\expandafter\sortitem\expandafter{\firstitem}
\expandafter\sortitem\expandafter{\seconditem}
\expandafter\sortitem\expandafter{\thirditem}
\expandafter\sortitem\expandafter{\fourthitem}
\end{sortedlist}
\end{document}
我添加了,\expandafter
因此宏被扩展,所以项目被很好地排序,除非它们具有类似的某些格式\textbf
,否则它们只会首先显示而不按任何字母顺序排列。
任何想法 ?
答案1
我建议添加结构;命令\newitem
(补充\useitem
)将把实际数据与格式指令分开。
\documentclass{article}
\usepackage{datatool}
\makeatletter
\newcommand{\sortitem}{\@ifnextchar\bgroup\@sortitem{\expandafter\@sortitem}}
\newcommand{\@sortitem}[2]{%
\DTLnewrow{list}% Create a new entry
\DTLnewdbentry{list}{description}{#1}% Add entry as description
\DTLnewdbentry{list}{formatting}{#2}% Add formatting info
}
\newenvironment{sortedlist}{%
\DTLifdbexists{list}{\DTLcleardb{list}}{\DTLnewdb{list}}% Create new/discard old list
}{%
\DTLsort{description}{list}% Sort list
\begin{itemize}%
\DTLforeach*{list}{\theDesc=description,\theForm=formatting}{%
\item \theForm{\theDesc}}% Print each item
\end{itemize}%
}
\newcommand{\newitem}[3]{\newcommand#1{{#2}{#3}}}
\newcommand{\useitem}[1]{\expandafter\@useitem#1}
\newcommand{\@useitem}[2]{#2{#1}}
\makeatother
\begin{document}
% I need to be able to edit these items and their formatting without changing the rest of my code
\newitem{\firstitem}{ISDYNSTP: Is dynamic time step used ?}{}
\newitem{\seconditem}{ISCDCA:}{\textit}
\newitem{\thirditem}{MVAR}{\textbf}
\newitem{\fourthitem}{IS2TL}{}
\useitem\seconditem % just for example
Sorted:
\begin{sortedlist}
\sortitem\firstitem
\sortitem\seconditem
\sortitem\thirditem
\sortitem\fourthitem
\sortitem{AAAAfirst}{\textsc}
\end{sortedlist}
\end{document}
答案2
改编@Werner 的第二部分回答对于您链接的问题以及可选的纯文本排序,以下内容可能适合您的需要。
对于每个格式化命令,您必须定义一个具有相同名称但带有后缀的单独命令,plain
该后缀定义用于排序的纯文本。该\sortitem
命令已被修改,因此如果传递了一个参数,则首先测试它是否是命令(由此改编形式提供回答由 egreg 提供)。如果存在,则plain
测试变体,如果存在,则将其用作排序标签。如果不存在,则使用传递的参数。
您的 MWE(附带一些额外的测试条目):
\documentclass{article}
\usepackage{datatool}% http://ctan.org/pkg/datatool
%from egreg at: https://tex.stackexchange.com/a/42337/89497
\makeatletter
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\macroname}[1]{\expandafter\removebs\string#1}
%adapted from https://tex.stackexchange.com/a/42337/89497
\newif\ifisamacro
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\nameifmacro#1#2\nil{\if#1|#2\else\relax\fi}}
\makeatother
\newcommand{\sortitem}[2][\relax]{%
\DTLnewrow{list}% Create a new entry
\ifx#1\relax%no option passed...see if #2 is a macro and has a plain-text variant
\edef\testifmacro{\expandafter\nameifmacro\string#2\nil}%test if it is a macro. will be the name if it is a macro, \relax if not
\expandafter\ifx\testifmacro\relax%then it is not a macro...
\DTLnewdbentry{list}{sortlabel}{#2}%
\else%it is a macro
\expandafter\ifcsname\macroname{#2}plain\endcsname%then it has a plain text option defined
\edef\rslt{\noexpand\DTLnewdbentry{list}{sortlabel}{\expandafter\expandafter\expandafter\expandonce\expandafter\csname\macroname{#2}plain\endcsname}}%
\else%no plain text option...expand macro once to pass contents for sorting
\edef\rslt{\noexpand\DTLnewdbentry{list}{sortlabel}{\expandonce#2}}%
\fi
\rslt%execute the \DTLnewdbentry command
\fi
\else
\DTLnewdbentry{list}{sortlabel}{#1}% Add entry sortlabel (optional argument)
\fi%
\DTLnewdbentry{list}{description}{#2}% Add entry description
}
\newenvironment{sortedlist}{%
\DTLifdbexists{list}{\DTLcleardb{list}}{\DTLnewdb{list}}% Create new/discard old list
}{%
\DTLsort{sortlabel}{list}% Sort list
\begin{itemize}%
\DTLforeach*{list}{\theDesc=description}{%
\item \theDesc}% Print each item
\end{itemize}%
}
\begin{document}
% I need to be able to edit these items and their formatting without changing the rest of my code
\newcommand{\firstitem}{ISDYNSTP: Is dynamic time step used ?}
\newcommand{\seconditem}{\textit{ISCDCA:}}
\newcommand{\thirditem}{\textbf{MVAR}}
\newcommand{\fourthitem}{IS2TL}
%append plain to the end of the associated macroname to define the plain text variant for it.
\newcommand{\seconditemplain}{ISCDCA:}
\newcommand{\thirditemplain}{MVAR}
Default:
\begin{itemize}
\item \firstitem
\item \seconditem
\item \thirditem
\item \fourthitem
\end{itemize}
Sorted:
\begin{sortedlist}
\sortitem{b}
\sortitem{\firstitem}
\sortitem{\seconditem}
\sortitem{seconditem}
\sortitem{\thirditem}
\sortitem{\fourthitem}
\sortitem{A}
\end{sortedlist}
\end{document}
产量:
包括初始测试,\sortitem{seconditem}
以便不是根据 的内容进行排序\seconditemplain
。我已尝试将参数和引用命令的扩展限制在其内容中,以sortlabel
防止其中断。因此,如果没有plain
提供变体,则它们将根据\
内容开头的 进行排序(据我所知)并将出现在列表的开头。如果命令plain
实际上不是纯文本,这也可以防止它中断。请注意,如果在左括号和命令之间有空格(例如\sorteditem{ \seconditem}
),它将中断。
一个不错的补充是将格式化和纯文本命令的创建包装到单个调用中,例如:
\newcommand{\newsorteditem}[3][\relax]{%
\ifx#1\relax\else
\expandafter\newcommand\expandafter{\csname#3plain\endcsname}{#1}%
\fi
\expandafter\newcommand\expandafter{\csname#3\endcsname}{#2}}%
其名称将类似于\newsorteditem[plain text]{formatted text}{commandname}
。
答案3
我找到了一种使用@Werner 方法的方法,而不必自己编写我试图排序的宏的纯文本版本。
它使用包\pdfstringdef
来hyperref
提取宏的纯文本形式。
使用此方法时要小心,因为它对任何格式都不安全,例如,我必须为 定义一个例外\textcolor
。它对空格也表现得很奇怪,但对我来说这不是问题。
以下是代码:
\documentclass{article}
\usepackage{datatool}
\usepackage{xcolor}
\usepackage{hyperref}
\newcommand{\sortitem}[2][\relax]{%
\DTLnewrow{list}% Create a new entry
\ifx#1\relax
\DTLnewdbentry{list}{sortlabel}{#2}% Add entry sortlabel (no optional argument)
\else
\DTLnewdbentry{list}{sortlabel}{#1}% Add entry sortlabel (optional argument)
\fi%
\DTLnewdbentry{list}{description}{#2}% Add entry description
}
\newenvironment{sortedlist}{%
\DTLifdbexists{list}{\DTLcleardb{list}}{\DTLnewdb{list}}% Create new/discard old list
}{%
\DTLsort{sortlabel}{list}% Sort list
\begin{itemize}%
\DTLforeach*{list}{\theDesc=description}{%
\item \theDesc}% Print each item
\end{itemize}%
}
\pdfstringdefDisableCommands{\def\textcolor#1{}}
\begin{document}
\newcommand{\first}{ISDYNSTP: Is dynamic time step used ?}
\newcommand{\second}{\textcolor{blue}{ISCDCA:}}
\newcommand{\third}{\textbf{MVAR}}
\newcommand{\fourth}{IS2TL}
List of items :
\begin{itemize}
\item \first
\item \second
\item \third
\item \fourth
\end{itemize}
\bigskip
\pdfstringdef\firstplain{\first}
\pdfstringdef\secondplain{\second}
\pdfstringdef\thirdplain{\third}
\pdfstringdef\fourthplain{\fourth}
List of "plain" items :
\begin{itemize}
\item \firstplain
\item \secondplain
\item \thirdplain
\item \fourthplain
\end{itemize}
\bigskip
Sorted items (without expandafter) :
\begin{sortedlist}
\sortitem[\firstplain]{\first}
\sortitem[\secondplain]{\second}
\sortitem[\thirdplain]{\third}
\sortitem[\fourthplain]{\fourth}
\end{sortedlist}
\bigskip
Sorted items (with expandafter) :
\begin{sortedlist}
\expandafter\sortitem\expandafter[\firstplain]{\first}
\expandafter\sortitem\expandafter[\secondplain]{\second}
\expandafter\sortitem\expandafter[\thirdplain]{\third}
\expandafter\sortitem\expandafter[\fourthplain]{\fourth}
\end{sortedlist}
\bigskip
\end{document}
正如您所看到的,如果您尝试该代码示例,您需要使用它\expandafter
来获得正确的结果。
下面是一个更复杂的例子,它可以处理要排序的宏列表。
\documentclass{article}
\usepackage{datatool}
\usepackage{xcolor}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{hyperref}
\makeatletter
\newcommand{\sortitem}[2][\relax]{%
\DTLnewrow{list}% Create a new entry
\ifx#1\relax
\DTLnewdbentry{list}{sortlabel}{#2}% Add entry sortlabel (no optional argument)
\else
\DTLnewdbentry{list}{sortlabel}{#1}% Add entry sortlabel (optional argument)
\fi%
\DTLnewdbentry{list}{description}{#2}% Add entry description
}
\newenvironment{sortedlist}{%
\DTLifdbexists{list}{\DTLcleardb{list}}{\DTLnewdb{list}}% Create new/discard old list
}{%
\DTLsort{sortlabel}{list}% Sort list
\begin{itemize}%
\DTLforeach*{list}{\theDesc=description}{%
\item \theDesc}% Print each item
\end{itemize}%
}
\pdfstringdefDisableCommands{\def\textcolor#1{}}
%%% Lists handling %%%
\newcommand{\addlocallist}{\listadd\locallists@dummy}
\NewDocumentCommand{\parsespacelist}{ >{\SplitList{ }} m } {%
\ProcessList{#1}{\addlocallist}%
}
\NewDocumentCommand{\parsecommalist}{ >{\SplitList{,}} m } {%
\ProcessList{#1}{\addlocallist}%
}
\newcommand{\parselist}[3][,]{%
\renewcommand\addlocallist{\listadd#3}%
\undef#3%
\ifstrequal{#1}{ }{\parsespacelist{#2}}{\parsecommalist{#2}}%
}
\newcommand{\addtosortedlist}[1]{%
\pdfstringdef\plaintexttemp{#1}%
\expandafter\sortitem\expandafter[\plaintexttemp]{#1}
}%
\newcommand{\ruleslist}[1]{%
\parselist[,]{#1}{\locallists@ruleslist}%
\begin{sortedlist}%
\forlistloop{\addtosortedlist}{\locallists@ruleslist}%
\end{sortedlist}%
}
\begin{document}
\newcommand{\first}{ISDYNSTP: Is dynamic time step used ?}
\newcommand{\second}{\textcolor{blue}{ISCDCA:}}
\newcommand{\third}{\textbf{MVAR}}
\newcommand{\fourth}{IS2TL}
List of items :
\begin{itemize}
\item \first
\item \second
\item \third
\item \fourth
\end{itemize}
\bigskip
Ordered list :
\ruleslist{\first, \second, \third, \fourth}
\end{document}