5

I am trying to parse arrays of strings, but when I create an array of containing a single string (say the string contains n characters), it is not parsed correctly, instead it's parsed as containing n items, with each item an individual character. can anyone shed some light on why does this happen? thanks in advance! Code and output below:

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

\noindent Array 1 is \{"this","is","cool"\}

\def\myArray{{"this","is","cool"}}

\pgfmathparse{dim(\myArray)}
\edef\myArrayLength{\pgfmathresult}

\pgfmathparse{\myArrayLength-1}
\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 1, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult \newline
}

\noindent Array 2 is \{"not cool"\}

\def\singleItemArray{{"not cool"}}

\pgfmathparse{dim(\singleItemArray)}
\edef\singleItemArrayLength{\pgfmathresult}

\pgfmathparse{\singleItemArrayLength-1}
\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 2, item \i: \pgfmathparse{\singleItemArray[\i]}\pgfmathresult \newline
}
\end{document}

enter image description here

  • 1
    Interestingly, there is no formal definition for arrays, just a few examples. It's not the space, because {{"notcool"}} produces the same issue. Length one arrays seem to be invalid, as {{12}} turns out to have length two as well. – egreg Oct 21 '17 at 22:38
  • To work around this bug/feature, you can enclose each element into braces: \def\myArray{{{"this"},{"is"},{"cool"}}}, \def\myArray{{{"not cool"}}}... – Paul Gaborit Oct 22 '17 at 07:29

3 Answers3

2

I would suggest to use xstring for a simple string item or listofitems for separated items:

\documentclass{article}
\usepackage{xstring}
\usepackage{pgffor}
\usepackage{listofitems}

\begin{document}


{\bfseries xstring on list:}\vspace{10pt}

\def\myarray{not cool}
\StrLen{\myarray}[\MyStrLen]

Items of "\myarray": \MyStrLen\par
\foreach \i in {1,...,\MyStrLen}{Item \i: ``\StrMid{\myarray}{\i}{\i}''\par}\vspace*{20pt}

{\bfseries listofitems on list:}\vspace{10pt}

\setsepchar{,}
\def\mylist{test item, {another item}, third item}
\readlist\commalist\mylist

Items of "\mylist":\commalistlen\par
\foreachitem\x\in\commalist{Item \xcnt: ``\x''\par}

\end{document}

Output:

enter image description here

Edit after comment:

For one item list, listofitems is still an option.

Adding the follow code on the above file:

\setsepchar{,}
\def\mylist{"not cool"}
\readlist\commalist\mylist

Items of "\mylist":\commalistlen\par
\foreachitem\x\in\commalist{Item \xcnt: ``\x''\par}\vspace{20pt}

We get the desired result:

enter image description here

PS: There is a problem if we enclose the item inside {} that I thing have to be reported to maintainer.

koleygr
  • 20,105
  • Aren't you missing the point here? The idea is to not parse the second one char by char. I think adding answers about a related issue just in case someone find this in a search while looking for something else is a bad idea. – Torbjørn T. Oct 21 '17 at 20:07
  • @TorbjørnT. You are right. But I wasn't really off topic. I just gave a solution on such problems (more for newbies). I edited and added an one item list parsed with listofitems... I still thing this is not the answer that the OP is looking for, but solves such problems (of pursing an one item list-array). I just found an error of the package that I will report to the maintainer so, that the list can be read exactly as the OP is giving it. – koleygr Oct 21 '17 at 21:40
  • 2
    The maintainer of listofitems (@Christian Tellechea) just informed me that he will try to fix the bug as soon as possible. – koleygr Oct 22 '17 at 08:08
1

I'm not sure this is a bug of PGF, as there's no formal definition of what an array is; but it turns out that an array must not have length one:

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

\verb|\def\myArray{{"this","is","cool"}}|
\def\myArray{{"this","is","cool"}}

\pgfmathsetmacro\myArrayLength{dim(\myArray)}

\pgfmathsetmacro\lastIndex{\myArrayLength-1}

\foreach \i in {0,...,\lastIndex} 
 {
  Array 1, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult\par
 }

\verb|\def\myArray{{"notcool"}}|
\def\myArray{{"notcool"}}

\pgfmathsetmacro\myArrayLength{dim(\myArray)}

\pgfmathsetmacro\lastIndex{\myArrayLength-1}

\foreach \i in {0,...,\lastIndex} 
 {
  Array 2, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult\par
 }

\verb|\def\myArray{{12}}|
\def\myArray{{12}}

\pgfmathsetmacro\myArrayLength{dim(\myArray)}

\pgfmathsetmacro\lastIndex{\myArrayLength-1}

\foreach \i in {0,...,\lastIndex} 
 {
  Array 3, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult\par
 }
\end{document}

enter image description here

The last example gives the same result as with \def\myArray{{1,2}}.

egreg
  • 1,121,712
1

I found a solution with an other array length command and some if...else statements to handle the single item arrays, which are causing problems.

Step 1: Compute the size of an array with code by @Alain Matthes

\def\getlen#1{%
\pgfmathsetmacro{\lenarray}{0}% 
\foreach \i in #1{%
 \pgfmathtruncatemacro{\lenarray}{\lenarray+1}% 
 \global\let\lenarray\lenarray}%
}  

Example: define array \def\mar{1,2},compute length \getlen{\mar} and get value \lenarray %=> '2'

Note: length of the array is stored in \r with \edef\r{\lenarray}.

Step 2: Expand the single array:

\ifnum\r=1
\def\myArray{{\myArrayRoh,"\relax "}}
\else
\def\myArray{{\myArrayRoh}}
\fi

Step 3: ... so you have to reduce the last item index with 2 instead of 1 for every single array:

\ifnum\r=1
\pgfmathparse{\myArrayLength-2} % single item
\else
\pgfmathparse{\myArrayLength-1} % multiple items
\fi

Result:

enter image description here

MWE:

\documentclass[varwidth,border=7]{standalone}
\usepackage{tikz}

% https://tex.stackexchange.com/a/66159/124842 (@Alain Matthes)
\def\getlen#1{%
\pgfmathsetmacro{\lenarray}{0}% 
\foreach \i in #1{%
\pgfmathtruncatemacro{\lenarray}{\lenarray+1}% 
\global\let\lenarray\lenarray}%
}   
\begin{document}
\def\myArrayRoh{"this"} % singleItemArray

\getlen{\myArrayRoh} 
\edef\r{\lenarray}

\ifnum\r=1
    \def\myArray{{\myArrayRoh,"\relax "}}
\else
    \def\myArray{{\myArrayRoh}}
\fi
%    
\pgfmathparse{dim(\myArray)}
\edef\myArrayLength{\pgfmathresult}

\ifnum\r=1
\pgfmathparse{\myArrayLength-2}
\else
\pgfmathparse{\myArrayLength-1}
\fi

\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 1, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult \newline
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\myArrayRoh{"this","is","cool"} % multiItemArray

\getlen{\myArrayRoh} 
\edef\r{\lenarray}

\ifnum\r=1
    \def\myArray{{\myArrayRoh,"\relax "}}
\else
    \def\myArray{{\myArrayRoh}}
\fi

\pgfmathparse{dim(\myArray)}
\edef\myArrayLength{\pgfmathresult}

\ifnum\r=1
\pgfmathparse{\myArrayLength-2}
\else
\pgfmathparse{\myArrayLength-1}
\fi

\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 2, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult \newline
}
\end{document}
Bobyandbob
  • 4,899