4

Given the following matrix

\def\points{{ {-1, 3}, {2, 7}, {3, 8} }}

which corresponds to the 3x2 matrix

enter image description here

how do I

  1. iterate (in tikz) over the number of columns?

  2. iterate (in tikz) over the number of rows?

  3. address an element directly, e.g. in row 1, column 0 (i.e. 2)

A similar question regarding an array of coordinates is posed at tikz array of coordinates: iteration and addressing elements

9

I am not sure about the current state of the function but there was an undocumented pgfmath function dim and you can use that. But please don't do this in TikZ, it doesn't have any consistency checks or even a data container type. So it is all wishful. Instead either use L3 capabilities (annoy them in chat if they don't provide an answer!) or call Lua or Python within TeX.

TikZ works with 0-indexing hence -1s in the row/col number calculations.

\documentclass{article}
\usepackage{pgfmath, pgffor}

\begin{document}
\def\points{{{-1, 3}, {2, 7}, {3, 8}}}

\pgfmathparse{\points[1][1]}\pgfmathresult% Get 1,1 element
\pgfmathtruncatemacro\myrows{dim(\points)-1}
\pgfmathtruncatemacro\mycols{dim(\points[0])-1}

\foreach\x in{0,...,\myrows}{% Over all rows
  \foreach\y in{0,...,\mycols}{% Over all columns
      \pgfmathparse{int(\points[\x][\y])}\pgfmathresult, 
    }%
}

\end{document}

enter image description here

  • 2
    I got annoyed. ;-) – egreg Dec 2 '17 at 0:37
7

You can use a simpler syntax.

\documentclass{article}
\usepackage{xparse}
\usepackage{pgffor}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\definematrix}{mm}
 {% #1 = symbolic name, #2 = list of rows
  \viesturs_matrix_define:nn { #1 } { #2 }
 }

\NewDocumentCommand{\showmatrix}{m}
 {
  \seq_clear:N \l_tmpa_seq
  \seq_map_inline:cn { l_viesturs_matrix_#1_seq }
   {
    \clist_set:Nn \l_tmpa_clist { ##1 }
    \seq_put_right:Nx \l_tmpa_seq { \clist_use:Nn \l_tmpa_clist { & } }
   }
  \begin{bmatrix}
  \seq_use:Nn \l_tmpa_seq { \\ }
  \end{bmatrix}
 }

\NewExpandableDocumentCommand{\matrixentry}{mmm}
 {% #1 = matrix name, #2 = row index, #3 = column index
  \viesturs_matrix_entry:nnn { #1 } { #2 } { #3 }
 }

\NewExpandableDocumentCommand{\matrixrows}{m}
 {
  \seq_count:c { l_viesturs_matrix_#1_seq }
 }

\NewExpandableDocumentCommand{\matrixcolumns}{m}
 {
  \int_use:c { l_viesturs_matrix_#1_int }
 }

\cs_new_protected:Nn \viesturs_matrix_define:nn
 {
  \seq_new:c { l_viesturs_matrix_#1_seq }
  \seq_set_split:cnn { l_viesturs_matrix_#1_seq } { ; } { #2 }
  \int_zero:N \l_tmpa_int
  \seq_map_inline:cn { l_viesturs_matrix_#1_seq }
   {
    \int_set:Nn \l_tmpa_int
     {
      \int_max:nn { \l_tmpa_int } { \clist_count:n { ##1 } }
     }
   }
  \int_new:c { l_viesturs_matrix_#1_int }
  \int_set:cn { l_viesturs_matrix_#1_int } { \l_tmpa_int }
 }

\cs_new:Nn \viesturs_matrix_entry:nnn
 {
  \clist_item:fn { \seq_item:cn { l_viesturs_matrix_#1_seq } { #2 } } { #3 }
 }

\cs_generate_variant:Nn \seq_set_split:Nnn { c }
\cs_generate_variant:Nn \clist_item:nn { f }

\ExplSyntaxOff

\definematrix{points}{-1,3;2,7;3,8}

\begin{document}

\matrixentry{points}{3}{2}

\foreach \x in {1,...,\matrixrows{points}}{% Over all rows
  \foreach \y in {1,...,\matrixcolumns{points}}{% Over all columns
    $\matrixentry{points}{\x}{\y}$,
  }%
}

$\showmatrix{points}$

\end{document}

enter image description here

  • 1
    So sweet but as usual years late for my thesis :-) – percusse Dec 2 '17 at 12:36
  • 1
    @percusse You should have been slow with your thesis like we know whom. – egreg Dec 2 '17 at 13:35
  • 2
    MY DUCK SENSE IS TINGLING – Paulo Cereda Dec 2 '17 at 14:08
  • Somebody talked about thesis? Hei, @PauloCereda!!!!!!! – Sigur Dec 2 '17 at 21:30
7

The listofitems package can parse such lists.

\documentclass{article}
\usepackage{listofitems}

\begin{document}
\setsepchar{;/,}
\readlist\mypoints{-1, 3; 2, 7; 3, 8} 
Second row elements are \mypoints[2,1], \mypoints[2,2].

\foreachitem\row\in\mypoints[]{%
  \foreachitem\col\in\mypoints[\rowcnt]{%
    Row \rowcnt, Column \colcnt{} is $\mypoints[\rowcnt,\colcnt]$\par
  }
}
\end{document}

enter image description here

If there is the requirement to have \points nested in the way described by the OP, then additional unpacking is needed to \reduce \points into \tmp, which takes the form of the above example:

\documentclass{article}
\usepackage{listofitems}
\def\reduce#1{\expandafter\reduceaux#1,\relax}
\def\reduceaux#1,#2\relax{\reduceauxaux#1%
  \ifx\relax#2\relax\else;\reduceaux#2\relax\fi%
}
\def\reduceauxaux#1{\reduceauxauxaux#1}
\def\reduceauxauxaux#1,#2{#1,#2}
\begin{document}
\def\points{{ {-1, 3}, {2, 7}, {3, 8} }}
\edef\tmp{\expandafter\reduce\points}

\detokenize\expandafter{\tmp}% THIS SHOWS CONVERSION OF \points INTO \tmp

\setsepchar{;/,}
\readlist\mypoints{\tmp} 
Second row elements are \mypoints[2,1], \mypoints[2,2].

\foreachitem\row\in\mypoints[]{%
  \foreachitem\col\in\mypoints[\rowcnt]{%
    Row \rowcnt, Column \colcnt{} is $\mypoints[\rowcnt,\colcnt]$\par
  }
}
\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.