9

I would like to define appropriate column types which suppress an entire row of a table if all of the columns (besides the title column, ie the first column) are empty. That is, with the tabular content as:

Label 1 & 62  & 79 \\
Label 2 &     &    \\
Label 3 &     & 31 \\
Label 4 & 45  &    \\

I would like to automatically suppress the row with Label 2.

My Attempt:

I defined a T column type at which time it is determined if the prior row is to be typeset via:

\newcommand{\TitleColumn}[1]{% We are starting a new row
    %% If there was a previous row that needed to be output, display that now
    \iftoggle{DisplayThisRow}{%
        \PrintTableTokens% Print prior row
        \global\togglefalse{DisplayThisRow}% Assume entire row is empty
    }{%
        %\vspace*{-\baselineskip}% Attempt to fix empty rows.
    }%
    \ResetTableTokens%
    \AddTableTokens{#1}% Start a new row with the row label
}

All other columns are of type L which sets the boolean DisplayThisRow to true upon encountering a non-empty cell:

\newcommand{\ColumnL}[1]{%
    \AddTableTokens{#1}% <--- Need a leading & here
    \IfStrEq{#1}{}{}{% Enable printing of row if non-empty content encoutered
        \global\toggletrue{DisplayThisRow}%
    }%
}

My attempt, based on

which yields

enter image description here

may not be the best approach. The current problems with it include:

  1. A blank like is output when a row is to be suppressed.

  2. The column separators are missing. Including a column separators in the call to \AddTableTokens{& #1} in the \ColumnL macro leads to

    Misplaced alignment tab character &.

  3. I was expecting a problem in that the last row would not be printed as there was no subsequent T column, but for some bizarre reason, the last row, Label 4, is printed first!

Code:

\documentclass{article}
\usepackage{collcell}
\usepackage{tabularx}
\usepackage{etoolbox}
\usepackage{xstring}
\usepackage{showframe}

%% This is based on:
%%    https://tex.stackexchange.com/q/165126/4301
%%    https://tex.stackexchange.com/q/175568/4301
%%
\makeatletter
    \newcommand*{\@MyTempTableTokens}{}%
    \newtoks\@tabtoks
    %%% assignments to \@tabtoks must be global, in case they are done in \foreach
    \newcommand\AddTableTokens[1]{\global\@tabtoks\expandafter{\the\@tabtoks#1}}
    \newcommand\eAddTableTokens[1]{% Do NOT add a tailing \\ for each line
        \protected@edef\@MyTempTableTokens{#1}%
        \expandafter\AddTableTokens\expandafter{\@MyTempTableTokens  \\}%
    }%
    %%% variable should be operated on always locally or always globally
    \newcommand*\ResetTableTokens{\global\@tabtoks{}}
    \newcommand*\PrintTableTokens{\the\@tabtoks}
\makeatother


\newcommand{\ColumnL}[1]{%
    \AddTableTokens{#1}% <--- Need a leading & here
    \IfStrEq{#1}{}{}{% Enable printing of row if non-empty content encoutered
        \global\toggletrue{DisplayThisRow}%
    }%
}

\newtoggle{DisplayThisRow}%
\togglefalse{DisplayThisRow}%
\newcommand{\TitleColumn}[1]{% We are starting a new row
    %% If there was a previous row that needed to be output, display that now
    \iftoggle{DisplayThisRow}{%
        \PrintTableTokens% Print prior row
        \global\togglefalse{DisplayThisRow}% Assume entire row is empty
    }{%
        %\vspace*{-\baselineskip}% Attempt to fix empty rows.
    }%
    \ResetTableTokens%
    \AddTableTokens{#1}% Start a new row with the row label
}

\newcolumntype{L}{>{\collectcell\ColumnL}l<{\endcollectcell}}
\newcolumntype{T}{>{\collectcell\TitleColumn}X<{\endcollectcell}}


\begin{document}
%\showcols
\noindent
\begin{tabularx}{\linewidth}{@{}TLL@{}} 
    Label 1 & 62  & 79 \\
    Label 2 &     &    \\
    Label 3 &     & 31 \\
    Label 4 & 45  &    \\
\end{tabularx}
\end{document}

1 Answer 1

11
+550

I use listofitems package to parse through the \BODY of the tabular. For each successive row, I search columns 2-n in search of data other than spaces. If found, I set \rowstatus to T. If the \rowstatus ends up T, I append the row's tokens to a macro (\tabdatatoks) for later use. Otherwise, I search the [failed] blank row's tokens for an \hline. If found, I also append that to the saved tokens list, even though the row is otherwise blank.

When done searching, I compose the tabular using the saved token list.

EDITED to handle additional row separators, such as \toprule, \midrule, and \bottomrule, in addition to \hline. Others can be easily added to the line \setsepchar{\hline||\bottomrule||\toprule}, with one proviso...the row separator cannot take an argument (e.g., \cline{1,3}).

REEDITED to properly handle instances of \cmidrule(x){x-x} and now \cline{x-x}. Any row separator that takes arguments other than \cmidrule and \cline will need extra code added.

\documentclass{article}
\usepackage{listofitems,environ,booktabs}
\makeatletter
\NewEnviron{mytab}[2][c]{%
  \setsepchar{\\/&}%
  \readlist\mytabdata{\BODY}%
  \def\tabdatatoks{}%
  \foreachitem\tabrow\in\mytabdata[]{%
    \gdef\rowstatus{F}%
    \foreachitem\tabcol\in\mytabdata[\tabrowcnt]{%
      \ifnum\tabcolcnt=1\relax\else%
        \setsepchar{ }%
        \readlist\mytabcell{\tabcol}%
        \foreachitem\cellpart\in\mytabcell[]{%
          \expandafter\ifx\expandafter\relax\cellpart\relax\else\gdef\rowstatus{T}\fi%
        }%
      \fi%
    }%
    \if T\rowstatus%
%     ADD NONEMPTY ROW DATA TO TOKEN LIST
      \expandafter\g@addto@macro\expandafter\tabdatatoks\expandafter{\tabrow\\}%
    \else%
%     FOR EMPTY ROWS, SEARCH FOR AND RETAIN ROW SEPARATOR LINES
      \setsepchar{\hline||\cline||\bottomrule||\midrule||\toprule||\cmidrule}%
      \expandafter\readlist\expandafter\rowsep\expandafter{\tabrow\ }%
      \foreachitem\x\in\rowsep[]{%
        \ifnum\xcnt=\listlen\rowsep[]\else%
%         ADD ROW SEPARATOR TO TOKEN LIST
          \expandafter\expandafter\expandafter\g@addto@macro%
            \expandafter\expandafter\expandafter\tabdatatoks%
            \expandafter\expandafter\expandafter{\rowsepsep[\xcnt]}%
%         SPECIAL PROVISIONS FOR ROW SEPARATORS WITH ARGUMENTS:
          \checkforsep{\cmidrule}{\getcmidruleargs}%
          \checkforsep{\cline}{\getclineargs}%
        \fi%
      }%
    \fi%
  }%
  \def\tmp{\begin{tabular}[#1]{#2}}\expandafter\tmp\tabdatatoks\end{tabular}%
}
\newcommand\checkforsep[2]{%
  \expandafter\expandafter\expandafter\ifx\rowsepsep[\xcnt]#1%
    \expandafter\expandafter\expandafter#2\rowsep[\numexpr\xcnt+1\relax]\relax\fi%
}
\def\getcmidruleargs(#1)#2#3\relax{\g@addto@macro\tabdatatoks{(#1){#2}}}
\def\getclineargs#1#2\relax{\g@addto@macro\tabdatatoks{{#1}}}
\makeatother
\begin{document}
\begin{mytab}{lrr}
\toprule
Label 1 & 62  & 79     \\
\cmidrule(r){1-1}\cmidrule(r){3-3}
Label 2 &     &        \\
Label 3 &     & 31     \\
\cline{1-1}\cline{3-3}
Label 4 &     &      \\
Label 5 & 45  & \today \\
\bottomrule
\end{mytab}\bigskip

For comparison, normal tabular:\medskip

\begin{tabular}{lrr}
\toprule
Label 1 & 62  & 79     \\
\cmidrule(r){1-1}\cmidrule(r){3-3}
Label 2 &     &        \\
Label 3 &     & 31     \\
\cline{1-1}\cline{3-3}
Label 4 &     &      \\
Label 5 & 45  & \today \\
\bottomrule
\end{tabular}
\end{document}

enter image description here

9
  • That looks very nice! Is it easy to expand the detection of \hline to include other separators? I was able to include \bottomrule by duplicating the lines in the \else clause but wondering if there is an easier way. Similarly, I was using \cmidrule(r){1-2} and wrapping that in a macro \newcommand*{\Divider}{\cmidrule(r){1-2}}, I was able to get it to work by again duplicating the three lines in the \else clause to check for \Divider. Commented Oct 24, 2018 at 17:50
  • @PeterGrill Yes, I've edited to make the process more customizable, but with one exception: it cannot handle row separators that take an argument, such as \cline{}. If that is a big issue, I can give it more thought. Commented Oct 24, 2018 at 18:02
  • Separators with parameter is not a big issue as I can just wrap it in a macro. However, it appears to work without requiring further modification. Simply defining \newcommand*{\Divider}{\cmidrule(r){1-2}} and using \Divider seems to be working. I was expecting to have to add \Divider to the list of possible separators, so wondering if I am missing something here? Also, \cmidrule(r){1-2} used directly seems to work too!!!! Huh? Commented Oct 24, 2018 at 18:09
  • 1
    @PeterGrill My guess is that, when using booktabs, don't use \hline, but rather \midrule. I am about to post the ultimate fix for the issue. Commented Oct 24, 2018 at 18:56
  • 1
    @PeterGrill I have provided support for \cline as well. Commented Oct 24, 2018 at 19:13

You must log in to answer this question.

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