![キーと値のペアからテーブルを生成する](https://rvso.com/image/330737/%E3%82%AD%E3%83%BC%E3%81%A8%E5%80%A4%E3%81%AE%E3%83%9A%E3%82%A2%E3%81%8B%E3%82%89%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B.png)
のようなキーと値のペアのコンマ区切りのリストからテーブルを自動的に生成したいと考えています{{x=1,y=2},{x=3,y=1},{x=2,y=3}}
。各「行」の「キー」は常に同じです (テーブルの列ヘッダーになります) が、値は変更できます。この特定のキーと値のペアのリストが与えられている場合、以下のコードは以下を生成します。
まさに私が望んでいたものです。残念ながら、 などの値に対するわずかに複雑な入力は、{{x=1,y=$\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
私のコードを壊します。私のコードは期待される出力を生成します:
しかし、これを実行する前に、次のようなコンパイル エラーが発生します。
! \tableRows の定義に不正なパラメータ番号があります。\crcr l.40 ...\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
これを行うためのより良い/より堅牢な方法はありますか?
これが私のコードです:
\documentclass{article}
\usepackage{xparse,etoolbox}
\newcounter{tableRow} % for counting the number of rows in the table
\newcounter{tableCol} % for counting the number of columns
\newcommand\tableRows{} % to hold the contents of the table
\newcommand\tableHeader{} % to hold the header of the table
\newcommand\MakeTable[1]{
\setcounter{tableRow}{0} % initialise
\setcounter{tableCol}{1}
\renewcommand\tableRows{}
\renewcommand\tableHeader{}
\forcsvlist\ProcessRow{#1}% generate table
\begin{tabular}{*{\arabic{tableCol}}{c}}
\tableHeader\\\hline\tableRows\\
\end{tabular}
}
\makeatletter
\newcommand\ProcessRow[1]{% generate table rows using \ProcessEntry
\addtocounter{tableRow}{1}
\protected@xappto\tableRows{\Alph{tableRow}}% row label in table
\forcsvlist\ProcessEntry{#1}
\protected@xappto\tableRows{\\}
}
% need to extract key-value pairs from input of the form: key=val
\newcommand\ExtractKeyValuePair[2]{\def\Key{#1}\def\Value{#2}}
\DeclareDocumentCommand\ProcessEntry{>{\SplitArgument{1}{=}}m}{% add entries to row
\ExtractKeyValuePair#1% key-value pairs --> \Key and \Value
\ifnum\value{tableRow}=1%
\addtocounter{tableCol}{1}
\protected@xappto\tableHeader{&\Key}
\fi
\protected@xappto\tableRows{&\Value}
}
\begin{document}
\MakeTable{{x=1,y=2},{x=3,y=1},{x=2,y=3}}
% \MakeTable fails on this example
\MakeTable{{x=1,y=$\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
\end{document}
テーブルの行を生成するために、私\protected@xappto
はツールボックスパッケージ。最初はトークンを使用しようとしましたが、無知が原因で拡張の問題が発生しました。\protected@xappto
ではなくが必要な理由がわかりませんが\xappto
、 を使用すると、\xappto
両方の例でコードが失敗します。
\SplitArgument
キーと値のペアを抽出する方法も少しやりすぎな感じがします\DeclareDocumentCommand
。xparseこれを行うにはパッケージが必要です。
編集
私のコードは大部分が機能し、私のMWEが抽出された実際のコードで最初に使用した例の1つであったという点で少し不運だったことが判明しました\overrightarrow
。私のMWEの問題は\overrightarrow
、壊れやすいコマンド。次の行を追加することで、MWE のコンパイル エラーを修正できます。
\usepackage{fixltx2e}
\MakeRobust{\overrightarrow}
ただし、これは完全な修正ではありません。コードを壊してしまう他の脆弱なコマンドが必ず存在するからです...テーブルを保存するのではなく生成するという Christian のアプローチが、おそらく最善の解決策でしょう。
答え1
kvsetkeys
コンマとキー値リストを解析するためのパッケージを使用するソリューション。テーブル仕様、ヘッダー行、および本体は、最初にトークン レジスタに構築されます。次に、テーブルが構成されます。
すべての割り当てはグループ内でローカルに保持されるため、\MakeTable
ネストできます。
このパッケージは、テーブル ヘッダー行の下に、より見やすい線を表示booktabs
します。\midrule
パッケージは、26 行を超えるアルファベット番号付けをalphalph
提供します。\AlphAlph
完全な例:
\documentclass{article}
\usepackage{booktabs}
\usepackage{kvsetkeys}
\usepackage{alphalph}
\makeatletter
% Resources
\newcount\TableRow
\newtoks\TableSpecification
\newtoks\TableHeader
\newtoks\TableBody
% Helper marco \AddToToks{<token register>}{<appended contents>}
\newcommand{\AddToToks}[2]{#1\expandafter{\the#1#2}}
% Main macro \MakeTable{...}
\newcommand{\MakeTable}[1]{%
\begingroup
\MakeTableSpecificationAndHeader{#1}%
\MakeTableBody{#1}%
\edef\TableBegin{%
\noexpand\begin{tabular}{\TableSpecification}%
}%
%\TableBegin
\begin{tabular}{\the\TableSpecification}%
\the\TableHeader
\midrule
\the\TableBody
\end{tabular}%
\endgroup
}
\newcommand{\MakeTableSpecificationAndHeader}[1]{%
\TableSpecification{l}%
\TableHeader{}%
\AnalyzeFirstRow{#1}%
\AddToToks\TableHeader{\tabularnewline}%
}
\newcommand{\AnalyzeFirstRow}[1]{%
\comma@parse{#1}\FirstRowProcessor
}
\newcommand{\FirstRowProcessor}[1]{%
\kv@parse{#1}\FirstRowCellsProcessor
\comma@break
}
\newcommand{\FirstRowCellsProcessor}[2]{%
\AddToToks\TableSpecification{c}%
\AddToToks\TableHeader{}%
}
\newcommand{\MakeTableBody}[1]{%
\TableRow=0 %
\TableBody{}%
\comma@parse{#1}\TableRowProcessor
}
\newcommand*{\TableRowProcessor}[1]{%
\advance\TableRow by 1 %
\edef\TableNumber{\AlphAlph{\TableRow}}%
\expandafter\AddToToks\expandafter\TableBody\expandafter{\TableNumber}%
\kv@parse{#1}\TableRowCellsProcessor
\AddToToks\TableBody{\tabularnewline}%
}
% Simplified implementation, which requires, that all keys of
% a row are given and have the correct order.
\newcommand{\TableRowCellsProcessor}[2]{%
\AddToToks\TableBody{}%
}
\makeatother
\begin{document}
\MakeTable{{x=1,y=2},{x=3,y=1},{x=2,y=3}}
\medskip
\MakeTable{{x=1,y=$\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
\medskip
\MakeTable{
{
xy=\MakeTable{{x=1, y=1}, {x=2, y=2}},
yz=foo,
},
{
xy=bar,
yz=\MakeTable{{y=4, z=5}, {y=6, z=7}},
},
}
\end{document}
答え2
これはキー値のないバージョンです (最初)。手動で行う必要があるのは、ここで列の数を調整することだけです。
\documentclass{article}
\usepackage{xparse,etoolbox}
\newcounter{tableRow} % for counting the number of rows in the table
\newcounter{tableCol} % for counting the number of columns
\newcommand\tableRows{} % to hold the contents of the table
\newcommand\tableHeader{} % to hold the header of the table
\ExplSyntaxOn
\clist_new:N \g_andrew_clist
\int_new:N \l_columncounter_int
\int_new:N \g_list_count
\NewDocumentCommand{\ProcessRow}{m}{%
\clist_gset:Nn \g_andrew_clist {#1}%
\int_gset:Nn \g_list_count {\clist_count:N \g_andrew_clist}
\int_gset:Nn \l_columncounter_int {\c_one}
\stepcounter{tableRow}%
\Alph{tableRow} &
\prg_replicate:nn { \g_list_count }{%
\int_compare:nNnTF { \l_columncounter_int } < { \g_list_count }{%
\clist_item:Nn \g_andrew_clist {\l_columncounter_int} &
}{
\clist_item:Nn \g_andrew_clist {\int_use:N \l_columncounter_int }
}
\int_gincr:N \l_columncounter_int
}
}
\newcommand\MakeTable[2][3]{%
\clist_set:Nn \l_tmpa_clist {#2}
\setcounter{tableRow}{0} % initialise
\setcounter{tableCol}{#1}
\begin{tabular}{*{\arabic{tableCol}}{c}}
& x & y \tabularnewline
\hline
\clist_map_inline:Nn \l_tmpa_clist {%
\ProcessRow{##1} \tabularnewline
}
\end{tabular}
}
\begin{document}
\ExplSyntaxOff
\MakeTable{ {1,2}, {5,6}, {3,{$\overrightarrow{AB}$}}}
\MakeTable[4]{ {1,2,3}, {4,5,6}, {7,8,{$\overrightarrow{AB}$}}}
\end{document}
アップデート
\documentclass{article}
\usepackage{xparse,etoolbox}
\usepackage{l3regex}
\newcounter{tableRow} % for counting the number of rows in the table
\newcounter{tableCol} % for counting the number of columns
\ExplSyntaxOn
\clist_new:N \g_andrew_argument_clist
\clist_new:N \g_andrew_clist
\clist_new:N \l_andrew_data_clist
\seq_new:N \l_andrew_header_seq
\int_new:N \l_columncounter_int
\int_new:N \g_list_count
\NewDocumentCommand{\ProcessRow}{m}{%
\clist_gset:Nn \g_andrew_clist {#1}%
\int_gset:Nn \g_list_count {\clist_count:N \g_andrew_clist}
\int_gset:Nn \l_columncounter_int {\c_one}
\stepcounter{tableRow}%
\Alph{tableRow} &
\prg_replicate:nn { \g_list_count }{%
\int_compare:nNnTF { \l_columncounter_int } < { \g_list_count }{%
\clist_item:Nn \g_andrew_clist {\l_columncounter_int} &
}{
\clist_item:Nn \g_andrew_clist {\int_use:N \l_columncounter_int }
}
\int_gincr:N \l_columncounter_int
}
}
\NewDocumentCommand{\ProcessHeader}{m}{%
\clist_gset_eq:NN \g_andrew_clist #1%
\int_gset:Nn \g_list_count {\clist_count:N \g_andrew_clist}
\int_gset:Nn \l_columncounter_int {\c_one}
& %
\prg_replicate:nn { \g_list_count }{%
\int_compare:nNnTF { \l_columncounter_int } < { \g_list_count }{%
\clist_item:Nn \g_andrew_clist {\l_columncounter_int} &
}{
\clist_item:Nn \g_andrew_clist {\int_use:N \l_columncounter_int }
}
\int_gincr:N \l_columncounter_int
}
}
\newcommand\MakeTable[1]{%
\clist_set:Nn \g_andrew_argument_clist {#1} % Store the full clist first
\clist_set:Nx \l_tmpb_clist {\clist_item:Nn \g_andrew_argument_clist {1}} % Extract the first line to get the header descriptions
\int_set:Nn \l_tmpa_int {\clist_count:N \l_tmpb_clist}
\int_incr:N \l_tmpa_int
\seq_clear:N \l_andrew_header_seq
\tl_set:Nn \l_tmpa_tl {#1} % Grab the argument again
\regex_replace_all:nnN { \w+= }{ } \l_tmpa_tl % Replace the x= values with nothing
\clist_set:NV \l_andrew_data_clist {\l_tmpa_tl} %making a new clist again
% Get the headers
\clist_map_inline:Nn \l_tmpb_clist { %
\tl_set:Nn \l_tmpa_tl {##1}
\seq_clear:N \l_tmpb_seq
\seq_set_split:NnV \l_tmpb_seq {=} {\l_tmpa_tl}
\seq_gput_right:Nx \l_andrew_header_seq {\seq_item:Nn \l_tmpb_seq {1}}
}
\clist_set_from_seq:NN \l_tmpb_clist \l_andrew_header_seq
\setcounter{tableRow}{0} % initialise
\setcounter{tableCol}{\int_use:N \l_tmpa_int}
\begin{tabular}{*{\int_eval:n{\l_tmpa_int+1}}{c}}
\ProcessHeader{\l_tmpb_clist} \tabularnewline % Header frist
\hline
\clist_map_inline:Nn \l_andrew_data_clist {%
\ProcessRow{##1} \tabularnewline
}
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\MakeTable{ {x=1,Foo=2}, {5,6}, {3,{$\overrightarrow{AB}$}}}
\MakeTable{ {x=1,y=2,z=3,u=4}, {x=4,y=5,z=6,u=10}, {x=7,y=8,{z=$\overrightarrow{AB}$},u=14}}
\end{document}
答え3
実際には を使用するの\protected@xappto
ではなく、\xappto
と を使用します\expandonce
。
(行末の保護も修正しました。)
\documentclass{article}
\usepackage{xparse,etoolbox}
\newcounter{tableRow} % for counting the number of rows in the table
\newcounter{tableCol} % for counting the number of columns
\newcommand\tableRows{} % to hold the contents of the table
\newcommand\tableHeader{} % to hold the header of the table
\newcommand\MakeTable[1]{%
\setcounter{tableRow}{0}% initialise
\setcounter{tableCol}{1}%
\renewcommand\tableRows{}%
\renewcommand\tableHeader{}%
\forcsvlist\ProcessRow{#1}% generate table
\begin{tabular}{*{\arabic{tableCol}}{c}}
\tableHeader\\\hline\tableRows\\
\end{tabular}%
}
\newcommand\ProcessRow[1]{% generate table rows using \ProcessEntry
\addtocounter{tableRow}{1}%
\xappto\tableRows{\Alph{tableRow}}% row label in table
\forcsvlist\ProcessEntry{#1}%
\gappto\tableRows{\\}%
}
% need to extract key-value pairs from input of the form: key=val
\newcommand\ExtractKeyValuePair[2]{\def\Key{#1}\def\Value{#2}}
\DeclareDocumentCommand\ProcessEntry{>{\SplitArgument{1}{=}}m}{% add entries to row
\ExtractKeyValuePair#1% key-value pairs --> \Key and \Value
\ifnum\value{tableRow}=1
\addtocounter{tableCol}{1}%
\xappto\tableHeader{&\expandonce{\Key}}%
\fi
\xappto\tableRows{&\expandonce{\Value}}%
}
\begin{document}
\MakeTable{{x=1,y=2},{x=3,y=1},{x=2,y=3}}
\MakeTable{{x=1,y=$\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
\end{document}
expl3
キー値インターフェースを使用する(または、おそらく乱用する)実装。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\MakeTable}{m}
{
\andrew_maketable_main:n { #1 }
}
\seq_new:N \l__andrew_maketable_cols_seq
\tl_new:N \l__andrew_maketable_body_tl
\int_new:N \l__andrew_maketable_rows_int
\keys_define:nn { andrew/maketable }
{
unknown .code:n = { \__andrew_maketable_add:n { #1 } }
}
\cs_new_protected:Nn \andrew_maketable_main:n
{
\seq_clear:N \l__andrew_maketable_cols_seq
\tl_clear:N \l__andrew_maketable_body_tl
\int_zero:N \l__andrew_maketable_rows_int
\clist_map_inline:nn { #1 }
{
\__andrew_maketable_add_row:n { ##1 }
}
\tl_put_left:Nx \l__andrew_maketable_body_tl
{ & \seq_use:Nn \l__andrew_maketable_cols_seq { & } \exp_not:n { \\ \hline } }
\begin{tabular}{ c *{ \seq_count:N \l__andrew_maketable_cols_seq } { c } }
\tl_use:N \l__andrew_maketable_body_tl
\end{tabular}
}
\cs_new_protected:Nn \__andrew_maketable_add_row:n
{
\int_incr:N \l__andrew_maketable_rows_int
\tl_put_right:Nx \l__andrew_maketable_body_tl
{
\int_to_Alph:n { \l__andrew_maketable_rows_int }
}
\keys_set:nn { andrew/maketable } { #1 }
\tl_put_right:Nn \l__andrew_maketable_body_tl { \\ }
}
\cs_new_protected:Nn \__andrew_maketable_add:n
{
\seq_if_in:NxF \l__andrew_maketable_cols_seq { \l_keys_key_tl }
{
\seq_put_right:Nx \l__andrew_maketable_cols_seq { \l_keys_key_tl }
}
\tl_put_right:Nn \l__andrew_maketable_body_tl { & #1 }
}
\ExplSyntaxOff
\begin{document}
\MakeTable{{x=1,y=2},{x=3,y=1},{x=2,y=3}}
\MakeTable{{x=1,y=$\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
\MakeTable{ {x=1,y=2,z=3,u=4}, {x=4,y=5,z=6,u=10}, {x=7,y=8,z=$\overrightarrow{AB}$,u=14}}
\end{document}
シーケンスは列ヘッダーを追跡し、トークン リスト変数に行を格納します。
もちろん、元の実装と同様に、順序は厳密でなければならず、そうでないとアイテムが間違った場所に置かれてしまいます。
答え4
パッケージを使用したアプローチxintツール。
他の実装と同様に、順序は厳密でなければなりません。キーの順序を任意にしたい場合は、より複雑なアプローチが必要です。
\documentclass{article}
\usepackage{xinttools}
% helper utilities
\newcommand*\AndrewExtractKey {}
\def\AndrewExtractKey #1#2=#3,{#1#2}
\newcommand*\AndrewExtractValue {}
\def\AndrewExtractValue #1=#2#3,{#2#3}
\makeatletter
\newcommand*\JohnDoeAlph [1]{\@Alph{#1\relax}}
\makeatother
% Main command
\newcommand*\MakeTable [1]{%
\fdef\AndrewTableKeys{\xintCSVtoList{#1}}%
%
\fdef\AndrewTableKeysFirstRow{\xintNthElt{1}{\AndrewTableKeys}}%
\fdef\AndrewTableKeysNbColumns
{\xintNthElt{0}{\xintCSVtoList{\AndrewTableKeysFirstRow}}}%
%
\gdef\AndrewTableKeysRowCount{1}%
\begin{tabular}{c*{\AndrewTableKeysNbColumns}c}
\xintFor ##1 in {\AndrewTableKeysFirstRow}\do
{&\AndrewExtractKey ##1,}\\
\hline
\xintFor* ##1 in {\AndrewTableKeys}\do
{%
\JohnDoeAlph{\AndrewTableKeysRowCount}%
\xdef\AndrewTableKeysRowCount{\the\numexpr\AndrewTableKeysRowCount+1}%
\xintFor ##2 in {##1}\do{&\AndrewExtractValue ##2,}\\%
}%
\end{tabular}
}
\begin{document}
\MakeTable{{x=1,y=2},{x=3,y=1},{x=2,y=3}}
\MakeTable{{x=1,y=$\overrightarrow{AB}$},{x=3,y=1},{x=2,y=3}}
\MakeTable{ {x=1,y=2,z=3,u=4}, {x=4,y=5,z=6,u=10}, {x=7,y=8,z=$\overrightarrow{AB}$,u=14}}
\end{document}