키-값 쌍에서 테이블 생성

키-값 쌍에서 테이블 생성

와 같이 쉼표로 구분된 키-값 쌍 목록에서 테이블을 자동으로 생성하고 싶습니다 {{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.\DeclareDocumentCommandxparse이를 수행하기 위한 패키지입니다.

편집하다

\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{&#1}%
}

\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{&#2}%
}
\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오히려 \xapptoand 를 사용하고 있습니다 \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}

인용구

관련 정보