![키-값 쌍에서 테이블 생성](https://rvso.com/image/330737/%ED%82%A4-%EA%B0%92%20%EC%8C%8D%EC%97%90%EC%84%9C%20%ED%85%8C%EC%9D%B4%EB%B8%94%20%EC%83%9D%EC%84%B1.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
내가 사용하는 테이블 의 행을 생성하려면etoolbox패키지. 처음에는 토큰을 사용해 보았지만 의심의 여지없이 무지로 인해 확장 문제가 발생했습니다. \protected@xappto
가 아닌 가 왜 필요한지 명확하지 않지만 코드를 \xappto
사용하면 두 예제 모두에서 실패합니다.\xappto
키-값 쌍을 추출하는 방식도 약간 OTT처럼 느껴 집니다 \SplitArgument
.\DeclareDocumentCommand
xparse이를 수행하기 위한 패키지입니다.
편집하다
\overrightarrow
내 코드는 대부분 작동하지만 MWE를 추출한 실제 코드에서 사용한 첫 번째 예제 중 하나라는 점에서 약간 운이 좋지 않았습니다 . 내 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
and 를 사용하고 있습니다 \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
패키지를 이용한 접근 방식xinttools.
다른 구현과 마찬가지로 순서는 엄격해야 합니다. 키의 임의 순서를 원하는 경우 더 복잡한 접근 방식이 필요합니다.
\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}