6

How can I get the next element in array that's being looped with \foreach?

Here is an example what I'd like to achieve, but it obviously doesn't work

\documentclass{article}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture} \def\points{(0.31,-0.23), (1,0.68), (0.54,1.35), (2,2)}

\foreach \p [count=\i] in \points{ \draw \points[\i] -- \points[\i+1]; } \end{tikzpicture}

\end{document}

Note that it's vital for me to use this kind of approach, because I want my code to be flexible, so that I could remove or add as many points in \points array as I want, so \draw (A) -- (B) -- (C) -- ... isn't suitable for me. And the actual task is a bit more complicated than a simple points connection, but it's based on the solution of this problem.

antshar
  • 4,238
  • 9
  • 30

3 Answers3

6

You can use remember to access the previous point.

\documentclass{article}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture} \def\points{(0.31,-0.23), (1,0.68), (0.54,1.35), (2,2)}

\foreach \p [count=\i,remember=\p as \lastp] in \points{ \ifnum\i>1\relax \draw \lastp -- \p; \fi } \end{tikzpicture}

\end{document}

  • Thanks! It's already something. May I wonder if it's possible to access \i+2, the last one or any other element in the array? – antshar Nov 06 '21 at 14:10
  • @antshar I think that this kind of array confuses pgf. The \foreach loop has a special switch for lists of coordinates, but pgf list operations seem to get confused by them. This means that you can in principle access elements of the list but would have to make some extra effort to undo the conversion pgf does to it. BTW, the above plot can also be produced with \draw plot coordinates {<list>};, where however the list should not contain commas between the coordinates, so it should rather be {(0.31,-0.23) (1,0.68) (0.54,1.35) (2,2)} in this case. –  Nov 06 '21 at 14:30
  • pgffor has special handling of pairs. pgfmanual, Sec. 88 "Repeating Things: The Foreach Statement" reads, "since pairs are such a natural and useful case, they get a special treatment by the \foreach statement. When a list item starts with a ( everything up to the next ) is made part of the item. – muzimuzhi Z Nov 06 '21 at 19:42
  • @muzimuzhiZ Thanks for the heads up! Yes, I am actually using this fact in the loop. However, I think that the question asking about the extraction of one (pair of) coordinate(s) from a list, and I was not able to convince the array function of pgf to do that (unless doing complicated stuff). Of course, one could use \foreach for that ... –  Nov 06 '21 at 20:18
  • Ya, pgfmath's array function takes more strict syntax. – muzimuzhi Z Nov 06 '21 at 20:45
  • @muzimuzhiZ do you know if it's possible to make such a parsing of coordinate pairs outside of \foreach. So that if I have the same \def\points{(0.31,-0.23), (1,0.68), (0.54,1.35), (2,2)}, how can I make a pgf array of it that can be iterated. For now I have to use multiple curly braces for the list (see my answer). – antshar Nov 07 '21 at 08:15
  • @antshar You could loop through \points and construct a new macro: \usepackage{etoolbox} \def\pointsBraced{} \foreach \p in \points { \xappto{\pointsBraced}{{\p},}}. – muzimuzhi Z Nov 08 '21 at 02:23
3

If for you it is not a problem to separate your points in the list with a char different from , (I used ;), because the comma is already used to separate the coordinates, it is very easy with listofitems package:

\documentclass{article}
\usepackage{tikz}
\usepackage{listofitems}
\setsepchar{;}

\begin{document}

\readlist\points{(0.31,-0.23); (1,0.68); (0.54,1.35); (2,2)} \begin{tikzpicture} \foreach \i in {2,...,\pointslen} {\draw \points[\i-1] -- \points[\i];} \end{tikzpicture}

With more points:

\readlist\points{(0.31,-0.23); (1,0.68); (0.54,1.35); (2,2); (3,4); (4,5)} \begin{tikzpicture} \foreach \i in {2,...,\pointslen} {\draw \points[\i-1] -- \points[\i];} \end{tikzpicture} \end{document}

enter image description here

CarLaTeX
  • 62,716
2

After some research, I guess I found the most suitable solution for me, that's why I mark this answer as 'accepted'. However, please consider other answers as well, because they might be more appropriate for your needs.

Here are two similar approaches:

\documentclass{article}
\usepackage{tikz}

\begin{tikzpicture}

\def\points{{{0.31,-0.23}, {1,0.68}, {0.54,1.35}, {2,2}}} \pgfmathsetmacro{\len}{dim(\points)-2}

\foreach \i [evaluate={\ax=\points[\i][0]; \ay=\points[\i][1]; \bx=\points[\i+1][0]; \by=\points[\i+1][1]}] in {0,...,\len}{ \draw (\ax,\ay) -- (\bx,\by); } \end{tikzpicture}

\end{document}

­

\documentclass{article}
\usepackage{tikz}

\begin{tikzpicture}

\def\points{{{0.31,-0.23}, {1,0.68}, {0.54,1.35}, {2,2}}} \pgfmathsetmacro{\len}{dim(\points)-2}

\foreach \i in {0,...,\len}{ \pgfmathsetmacro{\ax}{\points[\i][0]} \pgfmathsetmacro{\ay}{\points[\i][1]} \pgfmathsetmacro{\bx}{\points[\i+1][0]} \pgfmathsetmacro{\by}{\points[\i+1][1]} \draw (\ax,\ay) -- (\bx,\by); } \end{tikzpicture}

\end{document}

­

I think it worth referencing these posts that came in useful while looking for the solution:

Iteratively draw and connect nodes via `\foreach`

Draw a path between many nodes using foreach

Given an array of 2D points, how to access individual elements in TikZ?

2D tikz matrices: iteration and addressing elements

array of coordinates and array of strings

TikZ \foreach loop evaluate variable using pgfmath function

\foreach has a problem with ‘initially’ argument in remember part

pgfmathparse in foreach, use result as node coordinates

tikz foreach index in variable name

And also an example in subsection 94.2 Syntax for Mathematical Expressions: Operators at p. 1033 enter image description here

antshar
  • 4,238
  • 9
  • 30