How to count cells in tabular (horizontal and vertical)?
it looks a bit like a counter orgy ... for the columns count not all are needed, but it doesn't hurt.
\documentclass[a4paper,10pt]{article}
\usepackage{filecontents}
\begin{filecontents*}{doc.txt}
\r Jozko:Mrkvicka:010:100:111:
\r Ferko:Redkovka:110:100:111:
\r Dezo:Repka:011:100:001:
\r Julo:Petrzlen:011:100:101:
\r Ivan:Hrasok:110:101:010:
\r Peter:Fazulka:010:111:101:
\r Milan:Borovicka:010:101:100:
\r Anca:Malinova:010:110:110:
\end{filecontents*}
\usepackage{array,multido}
\multido{\iA=1+1}{9}{\newcounter{cntCol\iA}}% sum of the columns
\multido{\iA=1+1}{9}{\newcounter{cntRow\iA}}% sum of the rows
\newcounter{Col}\newcounter{Row}
\def\Short#1#2!!{#1. }
\def\Conv#1#2#3!!{%
\ifnum#1=0 -\else $\times$\stepcounter{cntCol\arabic{Col}} \fi\stepcounter{Col} &
\ifnum#2=0 -\else $\times$\stepcounter{cntCol\arabic{Col}} \fi\stepcounter{Col} &
\ifnum#3=0 -\else $\times$\stepcounter{cntCol\arabic{Col}} \fi\stepcounter{Col} }
\def\r#1:#2:#3:#4:#5:{%
\stepcounter{Row}%
\multido{\iA=1+1}{9}{\setcounter{cntCol\iA}{0}}%
\setcounter{Col}{1} & #2 \Short#1!! & \Conv#3!! & \Conv#4!! & \Conv#5!! &
\setcounter{Col}{0}\multido{\iA=1+1}{9}{%
\addtocounter{Col}{\expandafter\arabic{cntCol\iA}}%
\addtocounter{cntRow\iA}{\expandafter\arabic{cntCol\iA}}} \theCol \\}
\def\CRSum#1{\csname thecntRow#1\endcsname\addtocounter{Row}{\arabic{cntRow#1}}}
\begin{document}
\begin{tabular}{||c|l| *9{c}| c ||}\hline
\textrm{nb} & \textrm{LastName and FirstName} & \multicolumn{9}{c}{ \textrm{Lesson}} & $\sum$ \\ \cline{1-12}
\hline
\input{doc.txt}
\hline
& & \setcounter{Row}{0}%
\CRSum1 & \CRSum2 & \CRSum3 & \CRSum4 & \CRSum5 & \CRSum6
& \CRSum7 & \CRSum8 & \CRSum9 & \textbf{\theRow} \\
\hline
\end{tabular}
\end{document}
update (2017): last code sample used a non-public xint macro which has since been removed, hence the code was updated.
update (2015): this 2013 code needed some updates:
xinttools
package has been separated fromxint
and needs to be explicitely loaded for macros\xintFor
,\xintApply
, ...macro
\xintiSum
which was used in the three code samples here has since been renamed\xintiiSum
.usage of
\xintiAdd
was replaced by\xintiiAdd
for less overhead. Note: 1.2o 2017 release has deprecated\xintiAdd
anyhow.
This second (final) update inserts after my initial answer which used counters another approach without counters, far simpler than the "idiosyncratic way", described in the next paragraph and relegated to the the bottom of the answer. I have tried to comment the code to make it understandable. Also, I have picked up from egreg
the correct headers for the language settings, and the correct first row.
The update adds the "idiosyncratic way" without counters. It is based on a \Transpose
macro which is completely expandable and written in the style of the xint package code source, very hard to explain albeit short. The number of lessons is not hardcoded apart in the \r
macro, which could however be extended if needed to cope with an arbitrary dynamically determined number of lessons. [comments added to the slightly improved \Transpose
code]
Herbert's answer is surely to be preferred, but feeling adventurous I initially wanted to do this without any counters. However LaTeX's lack of a proper array structure demands to use frightening \csname..\endcsname
things when one wants to store data in enumerated things, and as I was going to make global assignments for the ultimate row, in the end the easiest is still to use counters, which are automatically global and may be named with digits in their names without further ado. But I use counters only for the totals of the last row.
It would be possible to prepare the whole thing without any counter nor global assignments, but this would give a bit too idiosyncratic code.
\documentclass[a4paper,10pt]{article}
\usepackage{filecontents}
\begin{filecontents*}{doc.txt}
\r Jozko:Mrkvicka:010:100:111:
\r Ferko:Redkovka:110:100:111:
\r Dezo:Repka:011:100:001:
\r Julo:Petrzlen:011:100:101:
\r Ivan:Hrasok:110:101:010:
\r Peter:Fazulka:010:111:101:
\r Milan:Borovicka:010:101:100:
\r Anca:Malinova:010:110:110:
\end{filecontents*}
% It works without array
% \usepackage{array}
% Loading array, it moves a bit the tabular
\usepackage{xint} % for \xintiiSum.
\usepackage{xinttools}% for \xintFor, \xintApply.
\xintFor* #1 in {\xintSeq {1}{9}}\do {\newcounter{Lesson#1}}
% \xintSeq {1}{9} expands to {1}{2}{3}{4}{5}{6}{7}{8}{9}
% One could have done also:
% \xintFor* #1 in {123456789}\do {\newcounter{Lesson#1}}
% I do that afterwards for efficiency,
\def\Short#1#2!!{#1. }
\def\r#1:#2:#3:#4:#5:{%
% Computations for the per column totals:
\xintAssignArray #3#4#5\to \PerLesson
% macro parameter character # must be doubled inside macros
\xintFor* ##1 in {123456789}
\do {\addtocounter {Lesson##1}{\PerLesson{##1}}}%
% Typesetting of the row corresponding to this student
& #2 \Short#1!! &
\xintFor* ##1 in {#3#4#5} \do {\ifcase##1 -\else $\times$ \fi &}
\xintiiSum{#3#4#5} \\
}
% Utility to compute the final big sum
\def\ValueLesson #1{\arabic{Lesson#1}}
\begin{document}
\begin{tabular}{||c|l| *9{c}| c ||}\hline
nb & LastName and FirstName & \multicolumn{9}{c}{Lesson} & $\sum$ \\
\hline
% STUDENT ROWS
% first we reset all counters to zero (if already used)
\xintFor* #1 in {123456789} \do {\setcounter{Lesson#1}{0}}
\input{doc.txt}
\hline
% LAST ROW
& & \xintFor* #1 in {123456789} \do {\arabic{Lesson#1} &}
\bfseries \xintiiSum {\xintApply\ValueLesson {123456789}}\\
\hline
\end{tabular}
\end{document}
A METHOD NOT USING COUNTERS (and trying to be not too complicated)
% these lines and the first table row picked up from egreg
\documentclass[a4paper,10pt]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[slovak]{babel}
% no need to anonymize, after all...
\usepackage{filecontents}
\begin{filecontents*}{doc.txt}
\r Jozko:Mrkvicka:010:100:111:
\r Ferko:Redkovka:110:100:111:
\r Dezo:Repka:011:100:001:
\r Julo:Petrzlen:011:100:101:
\r Ivan:Hrasok:110:101:010:
\r Peter:Fazulka:010:111:101:
\r Milan:Borovicka:010:101:100:
\r Anca:Malinova:010:110:110:
\end{filecontents*}
\usepackage{xint}% for \xintiiSum
\usepackage{xinttools}% for \xintFor, \xintListWithSep etc...
\makeatletter
\newcommand{\AddItemByItem}[2]
% expands completely in only two steps, and first expands its arguments
% Arguments: \ListA -> {x1}....{xN} each xi a number
% \ListB -> {y1}....{yN} with the same N
% Expands to {x1+y1}{x2+y2}....{xN+yN} with additions done by \numexpr
% NO check is done that the two arguments expand to the same number
% of items
% each item may be a macro \x, but must be {\x} if it comes first in the list
{\romannumeral0%
\expandafter\additembyitem@\expandafter {\romannumeral-`0#1}{#2}}
\def\additembyitem@ #1#2%
{\expandafter\additembyitem@a\expandafter{\expandafter}%
\expandafter{\romannumeral-`0#2!}#1!}
% #2 contains an ending !, also to avoid removal of braces if only one item
\def\additembyitem@a #1#2#3{\ifx!#3\expandafter\@firstoftwo
\else\expandafter\@secondoftwo \fi { #1}{\additembyitem@b {#1}{#3}#2.}}
% in #4 the yet to be treated items of the second initial list with a ! at the
% end to avoid brace removal.
\def\additembyitem@b #1#2#3#4.%
{\expandafter\additembyitem@c \expandafter{\the\numexpr #2+#3}{#1}{#4}}
\def\additembyitem@c #1#2{\additembyitem@a {#2{#1}}}
\makeatother % END OF DEFINITION OF \AddItemByItem
% token register for storing the rows via the \r things
% It would have been probably better not to have a \r in the input file,
% and to read the file line by line, because here the file is too much
% pre-formatted for LaTeX treatment
\newtoks\Rows
\def\Short#1#2!!{#1. }
\def\UpdateColTotals #1{%
% ON FIRST USE, INITIALIZES \ColTotals
\def\ColTotals {#1}%
% ON SUBSEQUENT USES, UPDATE \ColTotals
\def\UpdateColTotals ##1%
{\edef\ColTotals{\AddItemByItem{\ColTotals}{##1}}}%
% The tabular creates a group, so \UpdateColTotals could
% be used refreshed in other tabulars (or even in another cell of the same)
}
\def\r#1:#2:#3:#4:#5:{%
\Rows\expandafter{\the\Rows % rows up to now, and then the new row:
& #2 \Short#1!! &
\xintFor* ##1 in {#3#4#5} \do {\ifcase##1 -\else $\times$ \fi &}
\xintiiSum{#3#4#5} \\ }%
% Updating the column totals with the \AddItemByItem command
\UpdateColTotals {#3#4#5}%
}
\begin{document}\thispagestyle {empty}
\begin{tabular}{||c|l|*9{c}|c||}\hline
pč & Priezvisko a meno & \multicolumn{9}{c|}{cvičenie} & $\sum$ \\
\hline
\Rows {}%
\input{doc.txt}% All except the last row are now prepared
% Furthemore \ColTotals->{sum of col 1}{sum of col 2}....
\edef\lastrow {$\sum$ & & \xintListWithSep {&}{\ColTotals} &
\noexpand\bfseries \xintiiSum{\ColTotals} \noexpand \\ }
% now we can display the table, but \lastrow must be unpacked first
\expandafter\the\expandafter
\Rows
\expandafter
\hline
\lastrow
\hline
\end{tabular}
\end{document}
ANOTHER METHOD (more complicated)
This method relies on the construction of an expandable macro which manages to transpose any given matrix, in order to be able to compute column-per-column totals. This completely expandable macro is written in the style of the xint
source code.
\documentclass[a4paper,10pt]{article}
\usepackage{filecontents}
\begin{filecontents*}{doc.txt}
\r Jozko:Mrkvicka:010:100:111:
\r Ferko:Redkovka:110:100:111:
\r Dezo:Repka:011:100:001:
\r Julo:Petrzlen:011:100:101:
\r Ivan:Hrasok:110:101:010:
\r Peter:Fazulka:010:111:101:
\r Milan:Borovicka:010:101:100:
\r Anca:Malinova:010:110:110:
\end{filecontents*}
\usepackage{xint} % for \xintiiSum, \xintiiAdd
\usepackage{xinttools}% for \xintFor, \xintApply
% EXPANDABLE OPERATION OF TRANSPOSE OF A MATRIX
% THIS IS WRITTEN IN THE PECULIAR xint CODING STYLE
% the macro is completely expandable and expands in two steps
% input {first row}{second row}....{nth row}
% no error check is done that all rows must have the same length
% output {first column}{second column}... {mth column}
% All matrix elements will be braced on output even if single token on input
% No matrix element should be a \Z or \W token (even braced; the macro
% could be made more robust, but it uses \xintApply\macro\List which
% breaks if \List contains \Z as (even braced) item (xint 1.09d; \Z is used
% all over the place in xint macros doing computations where only digits are
% encountered, but its use in \xintApply is an error which will be added
% to the bug list )
\catcode`\_ 11
% update: \xint_relax was removed from xint code base around 1.2m release
\let\xint_relax\relax
\long\def\xint_gob_til_xint_relax #1\xint_relax{}%
\def\BraceIt #1{{#1}}%
\def\Transpose {\romannumeral0\transpose }%
\def\transpose #1{\expandafter\transpose_a\romannumeral-`0#1\xint_relax}%
\def\transpose_a #1{\xint_gob_til_xint_relax #1\transpose_empty\xint_relax
% if the thing is not empty: bracing each item of the first row is exactly what
% the transpose operation means on just one row. So we do this first and then we
% will progressively build up each row from these starting items
\expandafter\transpose_b\expandafter
{\romannumeral0\xintapply\BraceIt {#1}}}%
\def\transpose_empty #1#2#3#4#5{ }% stops the \romannumeral
% #2 = a row of the original matrix ; #1 = the earlier rows, transposed.
\def\transpose_b #1#2{\xint_gob_til_xint_relax #2\transpose_end\xint_relax
\transpose_c {}{#1\Z }#2\Z }%
% the #2 has been unbraced to reveal its items,
% The \Z following #1 is a trick to prevent brace pair removal in case of a
% single item in #1 in the loop which comes next
\def\transpose_c #1#2#3{\xint_gob_til_Z #3\transpose_nextrow\Z
\transpose_d {#1}{#3}#2\W }% #2 ends in \Z
\def\transpose_d #1#2#3#4\W {\transpose_c {#1{#3{#2}}}{#4}}% #4 keeps the \Z
\def\transpose_nextrow #1#2#3#4\W {\transpose_b {#3}}% #3 = the {#1} above
% we end up here when we have gobbled the \xint_relax
\def\transpose_end #1#2#3#4#5#6{\transpose_final \space #4}%
\def\transpose_final #1\Z {#1}% Thanks to the \space, no brace pair removal and
% also the \space will stop the \romannumeral
\catcode`\_ 8
\newtoks\Matrix
\newtoks\Rows
\newtoks\LastRow
\def\Short#1#2!!{#1. }
\def\r#1:#2:#3:#4:#5:{%
\Rows\expandafter{\the\Rows % rows up to now, and then the new row:
& #2 \Short#1!! &
\xintFor* ##1 in {#3#4#5} \do {\ifcase##1 -\else $\times$ \fi &}
\xintiiSum{#3#4#5} \\ }%
% The Matrix. We need it to transpose in order to compute the column totals.
\Matrix\expandafter{\the\Matrix {#3#4#5}}%
}
\begin{document}
\begin{tabular}{||c|l|*9{c}|c||}\hline
nb & LastName and FirstName & \multicolumn{9}{c}{Lesson} & $\sum$ \\
\hline
\def\TotalSum{0}% spaces here would be removed
\Rows {}% but I am too much use to comment out end of lines
\Matrix {}%
\LastRow {$\sum$ & & }%
\input{doc.txt}% \Rows is now mostly prepared
% ONLY REMAINS TO COMPUTE THE COLUMN TOTALS
\xintFor* #1 in % the grace of chaining completely expandable macros
{\xintApply\xintiiSum{\Transpose{\the\Matrix}}}
\do {%
\LastRow \expandafter{\the\LastRow #1 &}%
\edef\TotalSum {\xintiiAdd{\TotalSum}{#1}}% or \numexpr...
}%
\LastRow \expandafter\expandafter\expandafter
{\expandafter\the\expandafter\LastRow
\expandafter\bfseries \TotalSum \\}%
\expandafter\the\expandafter\Rows
\expandafter
\hline
\the\LastRow
\hline
\end{tabular}
\end{document}
This is admittedly rather complicated, but shows usage of several data structures of LaTeX3, namely sequences and property lists.
The data read from the external file are stored into a property list for each student, which might be reused several times.
There's no hardwired number of columns, because this number is computed dynamically.
So first we read the file and populate the property lists, named through the number identifying a student.
Then we build the table repeating all steps for each student. A sequence is maintained with the partial column sums, so it can be used for printing the last row.
Some slight complications are due to the initial \r
that must be removed and from the chosen delimiter. But all should be quite straightforward to follow.
\begin{filecontents*}{\jobname-list.txt}
\r Jozko:Mrkvicka:010:100:111:
\r Ferko:Redkovka:110:100:111:
\r Dezo:Repka:011:100:001:
\r Julo:Petrzlen:011:100:101:
\r Ivan:Hrasok:110:101:010:
\r Peter:Fazulka:010:111:101:
\r Milan:Borovicka:010:101:100:
\r Anca:Malinova:010:110:110:
\end{filecontents*}
\documentclass[a4paper,10pt]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[slovak]{babel}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\printtable}{m}
{
\jozko_read_table:n { #1 }
\jozko_print_table:
}
\tl_new:N \l_jozko_table_tl
\ior_new:N \g_jozko_read_ior
\seq_new:N \l_jozko_temp_seq
\tl_new:N \l_jozko_temp_tl
\int_new:N \g_jozko_student_int
\int_new:N \g_jozko_exercises_int
\int_new:N \l_jozko_sum_int
\seq_new:N \g_jozko_column_sums_seq
\seq_new:N \l_jozko_column_tempa_seq
\seq_new:N \l_jozko_column_tempb_seq
\tl_const:Nx \c_jozko_colon_tl { \tl_to_str:n { : } }
\tl_const:Nn \c_jozko_table_preamble_tl
{
\begin{tabular}{|c|l|*{\g_jozko_exercises_int}{c}|c|}
\hline
pč & Priezvisko~a~meno &
\multicolumn{\g_jozko_exercises_int}{c|}{cvičenie} & $\Sigma$ \\
\hline
}
\tl_const:Nn \c_jozko_table_postamble_tl
{
\hline
\end{tabular}
}
\cs_new_protected:Npn \jozko_read_table:n #1
{
\int_gzero:N \g_jozko_student_int
\ior_open:Nn \g_jozko_read_ior { #1 }
\ior_map_inline:Nn \g_jozko_read_ior { \jozko_process_name:n { ##1 } }
}
\cs_new_protected:Npn \jozko_process_name:n #1
{
\int_gincr:N \g_jozko_student_int
\seq_set_split:NVn \l_jozko_temp_seq \c_jozko_colon_tl { #1 }
\prop_new:c { g_jozko_student_ \int_to_arabic:n { \g_jozko_student_int } _prop}
\seq_pop_left:NN \l_jozko_temp_seq \l_jozko_temp_tl % get first name
\prop_gput:cnf { g_jozko_student_ \int_to_arabic:n { \g_jozko_student_int } _prop}
{ firstname } { \jozko_remove_r:V \l_jozko_temp_tl }
\seq_pop_left:NN \l_jozko_temp_seq \l_jozko_temp_tl % get last name
\prop_gput:cnV { g_jozko_student_ \int_to_arabic:n { \g_jozko_student_int } _prop }
{ lastname } \l_jozko_temp_tl
\tl_set:Nx \l_jozko_temp_tl { \seq_use:Nn \l_jozko_temp_seq { } }
\prop_gput:cnV { g_jozko_student_ \int_to_arabic:n { \g_jozko_student_int } _prop }
{ exercises } \l_jozko_temp_tl
\int_zero:N \l_jozko_sum_int
\tl_map_inline:Nn \l_jozko_temp_tl
{ \int_add:Nn \l_jozko_sum_int { ##1 } }
\prop_gput:cnf { g_jozko_student_ \int_to_arabic:n { \g_jozko_student_int } _prop }
{ sum } { \int_to_arabic:n { \l_jozko_sum_int } }
\int_gset:Nn \g_jozko_exercises_int
{
\int_max:nn { \g_jozko_exercises_int } { \tl_count:V \l_jozko_temp_tl }
}
}
\cs_new:Npn \jozko_remove_r:n #1
{
\__jozko_remove_r_aux:Nw #1 \q_stop
}
\cs_new:Npn \__jozko_remove_r_aux:Nw #1 #2 \q_stop
{
#2
}
\cs_new:Npn \jozko_print_table:
{
\seq_gclear:N \g_jozko_column_sums_seq
\int_step_inline:nnnn { 1 } { 1 } { \g_jozko_exercises_int }
{
\seq_gput_right:Nn \g_jozko_column_sums_seq { 0 }
}
\tl_set_eq:NN \l_jozko_table_tl \c_jozko_table_preamble_tl
\int_step_inline:nnnn { 1 } { 1 } { \g_jozko_student_int }
{
\tl_put_right:Nn \l_jozko_table_tl
{
\int_to_arabic:n { ##1 }. &
\prop_get:cn { g_jozko_student_ ##1 _prop }
{ lastname }
,~%
\prop_get:cn { g_jozko_student_ ##1 _prop }
{ firstname }
}
\seq_set_split:Nnx \l_jozko_column_tempa_seq { }
{
\prop_get:cn { g_jozko_student_ ##1 _prop } { exercises }
}
\seq_map_inline:Nn \l_jozko_column_tempa_seq
{
\tl_put_right:Nf \l_jozko_table_tl
{ \str_if_eq:nnTF { ####1 } { 0 } { & } { & $\times$ } }
}
\seq_clear:N \l_jozko_column_tempb_seq
\seq_mapthread_function:NNN
\g_jozko_column_sums_seq
\l_jozko_column_tempa_seq
\jozko_sum_entries:nn
\seq_gset_eq:NN \g_jozko_column_sums_seq \l_jozko_column_tempb_seq
\tl_put_right:Nx \l_jozko_table_tl
{
& \prop_get:cn { g_jozko_student_ ##1 _prop } { sum }
}
\tl_put_right:Nn \l_jozko_table_tl { \\ }
}
\tl_put_right:Nn \l_jozko_table_tl { \hline $\Sigma$ & & }
\tl_put_right:Nx \l_jozko_table_tl
{
\seq_use:Nn \g_jozko_column_sums_seq { & }
}
\tl_put_right:Nx \l_jozko_table_tl
{
&
\int_to_arabic:n { \seq_use:Nn \g_jozko_column_sums_seq { + } }
\exp_not:N \\
}
\tl_use:N \l_jozko_table_tl
\tl_use:N \c_jozko_table_postamble_tl
}
\cs_new_protected:Npn \jozko_sum_entries:nn #1 #2
{
\seq_put_right:Nx \l_jozko_column_tempb_seq
{
\int_to_arabic:n { #1 + #2 }
}
}
\cs_generate_variant:Nn \prop_gput:cnn { cnV }
\cs_generate_variant:Nn \prop_gput:cnn { cnf }
\cs_generate_variant:Nn \seq_set_split:Nnn { NV }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx }
\cs_generate_variant:Nn \tl_put_right:Nn { Nf }
\cs_generate_variant:Nn \jozko_remove_r:n { V }
\ExplSyntaxOff
\begin{document}
\printtable{\jobname-list.txt}
\end{document}