3

In my document I have many tables on the format

\begin{tabular}{l|l||l}
    a & b & W\\ \hline
    c & d & g\\ 
    e & f & g\\ 
\end{tabular}

The number of columns might vary, as can the number of rows but I always want to have a double line separating the rightmost column from the others and a line separating the first and second rows.

Is there some way that I can write a custom command allowing me to write something along the lines of

\begin{tabular2}{3}
    a & b & W\\
    c & d & g\\ 
    e & f & g\\ 
\end{tabular2}

For the same result?

This is so that I can ensure that I haven't made minor formatting errors changes in any of my many tables

4
  • Welcome to TeX.SX! Which engine are you using? pdflatex, xelatex, lualatex?
    – TeXnician
    Commented Oct 23, 2017 at 8:17
  • What should be the formatting of say 4 columns? Should it be {l|ll||l} or {l|l|l||l}? Are there always at least 3 columns? What should happen if there are less?
    – Skillmon
    Commented Oct 23, 2017 at 8:28
  • @Skillmon {||l} for the case of only one column. {l|l|l||l} for 4. There is always at least one column and one row.
    – Bomaz
    Commented Oct 23, 2017 at 8:35
  • @TeXnician pdflatex
    – Bomaz
    Commented Oct 23, 2017 at 8:36

4 Answers 4

7

With xparse (see edit history for versions prior to 2019-03-05):

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentEnvironment{bomaztab}{ O{c} m +b }
 {
  \bomaz_maketable:nnn { #1 } { #2 } { #3 }
 }
 {}

\tl_new:N \l_bomaz_preamble_tl
\tl_new:N \l_bomaz_firstrow_tl
\seq_new:N \l_bomaz_body_seq

\cs_new_protected:Nn \bomaz_maketable:nnn
 {
  % #1 = alignment argument, #2 = number of cols, #3 = body
  % add #2-1 'l|' cols
  \tl_set:Nx \l_bomaz_preamble_tl { \prg_replicate:nn { #2 - 1 }{ l| } }
  % add a trailing '|l'
  \tl_put_right:Nn \l_bomaz_preamble_tl { |l }
  % split the body at \\
  \seq_set_split:Nnn \l_bomaz_body_seq { \\ } { #3 }
  % detach the first row
  \seq_pop_left:NN \l_bomaz_body_seq \l_bomaz_firstrow_tl
  % start the tabular
  \bomaz_tabular:nV { #1 } \l_bomaz_preamble_tl
  % first row is followed by \hline
  \l_bomaz_firstrow_tl \\ \hline
  % deliver the other rows
  \seq_use:Nn \l_bomaz_body_seq { \\ }
  % end the tabular
  \endtabular
 }

% helper function to be varied (start of tabular)
\cs_new_protected:Nn \bomaz_tabular:nn { \tabular[#1]{#2} }
\cs_generate_variant:Nn \bomaz_tabular:nn { nV }

\ExplSyntaxOff

\begin{document}

\begin{bomaztab}{3}
    a & b & W\\
    c & d & g\\ 
    e & f & g\\ 
\end{bomaztab}
\qquad
\begin{bomaztab}{2}
    a & W \\
    c & g \\ 
    e & g \\ 
\end{bomaztab}

\medskip

XYZ
\begin{bomaztab}[t]{2}
    a & W \\
    c & g \\ 
    e & g \\ 
\end{bomaztab}

\end{document}

enter image description here

A slightly different implementation where the number of columns is guessed from the first row; however, columns with different alignments can be specified explicitly, see the last example; also the vertical alignment can be set independently.

\documentclass{article}
\usepackage{xparse,environ}

\ExplSyntaxOn

\NewDocumentEnvironment{bomaztab}{ O{} +b }
 {
  \bool_set_true:N \l_bomaz_guess_bool
  \keys_set:nn { bomaz/tables }
   {
    valign=c,
    #1
   }
  \bomaz_maketable:VVn \l_bomaz_align_tl \l_bomaz_preamble_tl { #2 }
 }
 {}

\bool_new:N \l_bomaz_guess_bool
\tl_new:N \l_bomaz_align_tl
\tl_new:N \l_bomaz_preamble_tl
\tl_new:N \l_bomaz_firstrow_tl
\seq_new:N \l_bomaz_body_seq
\seq_new:N \l__bomaz_preamble_seq
\seq_new:N \l__bomaz_temp_seq

\keys_define:nn { bomaz/tables }
 {
  valign .tl_set:N = \l_bomaz_align_tl,
  preamble .code:n =
   {
    \bool_set_false:N \l_bomaz_guess_bool
    \tl_set:Nn \l_bomaz_preamble_tl { #1 }
   },
 }

\cs_new_protected:Nn \bomaz_maketable:nnn
 {
  % #1 = alignment argument, #2 = preamble, #3 = body
  % split the body at \\
  \seq_set_split:Nnn \l_bomaz_body_seq { \\ } { #3 }
  % detach the first row
  \seq_pop_left:NN \l_bomaz_body_seq \l_bomaz_firstrow_tl
  \bool_if:NT \l_bomaz_guess_bool
   {
    \__bomaz_preamble_guess:
   }
  \__bomaz_preamble_compute:
  % start the tabular
  \bomaz_tabular:nV { #1 } \l_bomaz_preamble_tl
  % first row is followed by \hline
  \l_bomaz_firstrow_tl \\ \hline
  % deliver the other rows
  \seq_use:Nn \l_bomaz_body_seq { \\ }
  % end the tabular
  \endtabular
 }

% helper function to be varied (start of tabular)
\cs_new_protected:Nn \bomaz_tabular:nn { \tabular[#1]{#2} }
\cs_generate_variant:Nn \bomaz_tabular:nn { nV }
% variant
\cs_generate_variant:Nn \bomaz_maketable:nnn { VVn }

\cs_new_protected:Nn \__bomaz_preamble_guess:
 {
  \seq_set_split:NnV \l__bomaz_temp_seq { & } \l_bomaz_firstrow_tl
  \tl_set:Nx \l_bomaz_preamble_tl
   {
    \prg_replicate:nn { \seq_count:N \l__bomaz_temp_seq } { l }
   }
 }
\cs_new_protected:Nn \__bomaz_preamble_compute:
 {
  \seq_set_split:NnV \l__bomaz_preamble_seq { } \l_bomaz_preamble_tl
  \tl_set:Nx \l_bomaz_preamble_tl
   {
    \seq_use:Nnnn \l__bomaz_preamble_seq { || } { | } { || }
   }
 }
\ExplSyntaxOff

\begin{document}

\begin{bomaztab}
    a & b & W\\
    c & d & g\\ 
    e & f & g\\ 
\end{bomaztab}
\qquad
\begin{bomaztab}
    a & W \\
    c & g \\ 
    e & g \\ 
\end{bomaztab}

\medskip

XYZ
\begin{bomaztab}[valign=t,preamble=rl]
    aaa & WWW \\
    cc & gg \\ 
    e & ggg \\ 
\end{bomaztab}

\end{document}

enter image description here

5
  • 1
    If you use expl3 and environ you could as well parse the number of columns... Just saying :)
    – Skillmon
    Commented Oct 23, 2017 at 11:51
  • @Skillmon Do you mean calling \begin{bomaztab}{ccrllc} and get the equivalent of \begin{tabular}{c|c|r|l|l||c}?
    – egreg
    Commented Oct 23, 2017 at 11:57
  • No I meant you should just have to call \begin{bomaztab} and it knows the correct amount of columns (with the option to give it the correct amount of columns for edge cases where the alignment tab character might be hidden inside unexpandable macros).
    – Skillmon
    Commented Oct 23, 2017 at 14:56
  • @Skillmon That's a nice idea, let me work it out (train is 30 minutes late and the lounge has WiFi).
    – egreg
    Commented Oct 23, 2017 at 17:05
  • @Skillmon New implementation on-line.
    – egreg
    Commented Oct 23, 2017 at 17:45
4

You could use something like the following. Notice that the method to put a \hline under the first row is not pretty, imho.

\documentclass[]{article}

\newcount\tabtwocnt

\makeatletter
\newenvironment*{tabular2}[2][]
{%
  \def\tabtwotmpa{\begin{tabular}[#1]}%
  \ifnum#2=1\relax%
    \def\tabtwotmpb{||l}
  \else%
    \def\tabtwotmpb{}
    \tabtwocnt=1\relax%
    \loop%
      \edef\tabtwotmpb{\tabtwotmpb l|}%
      \advance\tabtwocnt by 1\relax%
      \ifnum\tabtwocnt<#2\relax%
    \repeat%
    \edef\tabtwotmpb{\tabtwotmpb|l}
  \fi%
  \global\let\tabtwotmpc\\
  \expandafter\tabtwotmpa\expandafter{\tabtwotmpb}%
  \global\let\tabtwotmpd\\%
  \gdef\\{\global\let\\\tabtwotmpd\\\hline}\@firstofone%
}{%
  \end{tabular}
  \global\let\\\tabtwotmpc
}
\makeatother

\begin{document}
\begin{tabular2}[]{1}
  a
\end{tabular2}
\begin{tabular2}[]{2}
  a & b
\end{tabular2}
\begin{tabular2}[]{3}
  a & b & c\\
  d & e & f
\end{tabular2}
\begin{tabular2}[]{4}
  a & b & c & z\\
  d & e & f & z
\end{tabular2}
\begin{tabular2}[]{5}
  a & b & c & y &z\\
  d & e & f & y &z
\end{tabular2}
\begin{tabular2}[]{12}
  a & b & c & y & z & 1 & 2 & 3 & 4 & 5 & 6 & 7\\
  a & b & c & y & z & 1 & 2 & 3 & 4 & 5 & 6 & 7\\
  a & b & c & y & z & 1 & 2 & 3 & 4 & 5 & 6 & 7
\end{tabular2}
Foo\\Bar
\end{document}

enter image description here

2

With listofitems and token lists. No number-of-columns argument is required.

\documentclass{article}
\usepackage{listofitems,environ}
\newtoks\tabAtoks
\newtoks\tabAhead
\newcommand\apptotoks[2]{#1\expandafter{\the#1#2}}
\NewEnviron{tabularA}[1][c]{%
  \setsepchar{\\/&}%
  \readlist\tabA{\BODY}%
  \tabAtoks{}%
  \foreachitem\i\in\tabA[]{%
    \ifnum\listlen\tabA[\icnt]>1\relax%
      \tabAhead{}%
      \foreachitem\j\in\tabA[\icnt]{%
        \ifnum\jcnt=1\relax\else\apptotoks\tabAhead{|}\fi%
        \ifnum\jcnt=\listlen\tabA[\icnt]\relax\apptotoks\tabAhead{|}\fi%
        \apptotoks\tabAhead{l}%
        \ifnum\jcnt=1\relax\else\apptotoks\tabAtoks{&}\fi%
        \expandafter\apptotoks\expandafter\tabAtoks\expandafter{\j}%
      }%
      \ifnum\icnt<\listlen\tabA[]\relax\apptotoks\tabAtoks{\\}\fi%
      \ifnum\icnt=1\relax\apptotoks\tabAtoks{\hline}\fi%
    \fi%
  }%
  \def\tmp{\begin{tabular}[#1]}
  \expandafter\tmp\expandafter{\the\tabAhead}%
  \the\tabAtoks
  \end{tabular}
}
\begin{document}
\begin{tabularA}
    a & b & W\\
    c & d & g\\ 
    e & f & g\\
\end{tabularA}
\qquad
\begin{tabularA}
    a & W \\
    c & g \\ 
    e & g \\
\end{tabularA}

\medskip

XYZ
\begin{tabularA}[t]
    a & W \\
    c & g \\ 
    e & g \\
\end{tabularA}
\end{document}

enter image description here

0

I do precisely this using TeXstudio's macros, and I imagine most editors have a similar macro or snippet functionality.

In TeXstudio, it's as simple as pasting your code into a box and assigning it a name:

  • Go to Macros > Edit Macros.
  • Click the Add button.
  • Put your table template text in the LaTeX Content field, selecting Type = Normal.
  • Put the name you want to appear on the TeXstudio menu in the Name field.
  • Optionally, you can define trigger text (Trigger field) which, when typed, will insert your table template.

To use it, go to the Macros menu in TeXstudio and select the name you added to the Name field above.

In the case of frequently used items, I also assign a shortcut key combination, so inserting, say, a table or figure or section header is as easy as CTRL+SHIFT+T or whatever:

  • Go to Options > Configure TeXstudio....
  • Select the Shortcuts tab.
  • Go to the entry for the Macro menu.
  • Expand it and select the macro you just created.
  • Double click in the Current Shortcut field, press your desired key combination, and hit ENTER.

Note that in TeXstudio, shortcuts seem to be positional: if you assign CTRL+Q to your second macro, "Insert Cool Stuff", and then move this macro elsewhere in the editable list, CTRL+Q will activate whatever ends up being in the second position in your macro list, and not "Insert Cool Stuff". This is an odd design choice, so you should be aware of it.

I realize you were looking at using a Latex macro (i.e. \newcommand\whatever) for this purpose, but as is so often the case, Latex makes the impossible easy and the easy excruciating, only solvable through a profound understanding of ancient programming, or both.

Your editor has no such problems ;-)

(EDIT: Considering the plethora of packages --thousands of lines of code, at least-- which have been written just to get basic table functionality to work right in Latex (by-row formatting, multi-columns, expanding cells, vertical alignment settings, and even that most basic of all functions, text wrapping, which worked just fine on my Atari 800 without any hacks), I stand by my statement about having to have a deep knowledge of ancient programming to do what is easy in other software (and any language that treats _ as a reserved character is ancient, if not pre-historic). That doesn't mean Latex is bad, but it is perhaps the single most arcane thing anyone not working on legacy bank mainframes will ever come across).

12
  • 2
    (a) There's no need for "ancient programming" (if you do not like TeX, try the very recent "expl3". (b) Answers should provide some details. How did you achieve a TeXstudio macro for arbitrary column numbers etc.? That would be a point to start.
    – TeXnician
    Commented Oct 23, 2017 at 8:45
  • @TeXnician Actually, and in spite of the other answers that have been given here, I'd say there's no need for programming of any type in this case. We're talking, essentially, about pasting a few code snippets into the editor -- most editors excel at this, and there's absolutely no need for scores of lines of code like \def\tabtwotmpa{\begin{tabular}[#1]}%! Commented Oct 23, 2017 at 9:41
  • 3
    Will this work for an arbitrary number of columns, like the other answers do? Commented Oct 23, 2017 at 9:52
  • @GilWilliams \begin{tabular2}{12} works just fine on my machine. My code works for an arbitrary number of columns (as long as TeX's capacities are not superseded).
    – Skillmon
    Commented Oct 23, 2017 at 11:14
  • @GilWilliams Both \begin{tabular2}[]{8} or \begin{bomaztab}{12} work perfectly fine. Commented Oct 23, 2017 at 11:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .