When playing with Ti*k*Z one often has to deal with lists, such as lists of coordinates, styles, whatever. This leads to a lot of repetetive work. `pgf` simplifies some of the things with `foreach` loops and `/.list` key handler. However, sometimes one wishes to count the items in the lists, add, or remove items and perhaps even sort them. There exist a couple of good tools for at least some of the tasks, such as * `etoolbox`, * `pgfplotstable`, * `datatool`, * `listofitems`, and probably many others. Each of them has pros and cons, e.g. `etoolbox` seems to be rather reliable but does not seem to have a sorting function. What I have done so far was to add some simple-minded pgf key handlers. (They are probably not going to stay in this precise form, but this is to have a concrete example.) For instance, one can define a list with ``` \pgfkeys{/my lists/.cd, tikzlings/.initial={anteater,bear,bee,cat,coati,hippo,koala,marmot,mouse}} ``` and then add an item with ``` \pgfkeys{/my lists/tikzlings/.add item=wolpertinger} ``` However, if one wants to go beyond that, quickly things become hacky. Removing an item is still straightforward, ``` \pgfkeys{/my lists/tikzlings/.remove item=mouse} ``` but the implementation relies on `\@removeelement` which may or may not be a good choice. Sorting is even worse, and all I really did was to implement some slow bubble sort for numbers. Certainly there must be better tools. I have not even started to really think about lists that contain macros or tokens that can or should not be expanded. So the question is: > are there reliable, not too extensive, well documented tools available that could be blended into a pgf machinery of this sort? Here, reliable means that spaces and macros are dealt with in a reasonable way. Well documented means that there is a description along with examples that allow users to learn this stuff. Not too extensive means no huge package system, if I wanted to do that probably `pgfplotstable` would have most of the things that could be relevant. And here is a code with examples that might illustrate one possible way of adding a list manager to pgf. ``` \documentclass[fleqn]{article} \usepackage{pgffor} \makeatletter \newif\if@pgf@list@first@item \pgfkeys{/handlers/.add item/.code=\pgfkeys{\pgfkeyscurrentpath/.append={,#1}}, /handlers/.remove item/.code={% \edef\pgfutil@tmpa{\pgfkeysvalueof{\pgfkeyscurrentpath}}% \edef\pgfutil@tmpb{#1}% \edef\pgfutil@tmpc{\noexpand\@removeelement{\pgfutil@tmpb}{\pgfutil@tmpa}{\noexpand\pgfutil@tmpa}}% \pgfutil@tmpc \pgfkeyssetvalue{\pgfkeyscurrentpath}{\pgfutil@tmpa}},% maybe test if the list is now empty? /handlers/.count/.code={\begingroup% \edef\pgfutil@tmpl{\pgfkeysvalueof{\pgfkeyscurrentpath}}% \c@pgf@counta0\relax \pgfkeys{/list management/step count/.list/.expanded={\pgfutil@tmpl}}% \edef#1{\the\c@pgf@counta}% \pgfmathsmuggle#1\endgroup},% /handlers/.print list/.code={\begingroup% \@pgf@list@first@itemtrue \edef\pgfutil@tmpl{\pgfkeysvalueof{\pgfkeyscurrentpath}}% \pgfkeys{/list management/print item in list/.list/.expanded={\pgfutil@tmpl}}% \endgroup}, /handlers/.is array/.code={\pgfkeysifdefined{\pgfkeyscurrentpath}{% \typeout{The key `\pgfkeyscurrentpath` is already in use, and won't get overwritten.}}{% \pgfkeysifdefined{\pgfkeyscurrentpath/dim}{% \typeout{The key `\pgfkeyscurrentpath` is already in use, and won't get overwritten.}}{% \c@pgf@counta=0\relax \pgfkeyssetevalue{/list management/current array}{\pgfkeyscurrentpath}% \pgfkeys{/list management/store array entry/.list/.expanded={#1}}% \pgfkeys{\pgfkeysvalueof{/list management/current array}/dim/.initial=\the\c@pgf@counta}% \pgfkeys{\pgfkeysvalueof{/list management/current array}/content/.initial={#1}}% }}}, /handlers/.sort numeric list/.code 2 args={\begingroup% \edef\pgfutil@tmpl{\pgfkeysvalueof{\pgfkeyscurrentpath}}% \edef\pgfutil@tmpa{0}% \pgfutil@for\pgfutil@tmpb:={\pgfutil@tmpl}\do{% build an indexed array and \edef\pgfutil@tmpa{\the\numexpr\pgfutil@tmpa+1}% precompute expressions used in comparison \ifcase\pgfutil@tmpa \or \or \edef\pgfutil@tmph{2}% \else \edef\pgfutil@tmph{\pgfutil@tmph,\pgfutil@tmpa}% \fi \pgfkeys{/list management/sort map=\pgfutil@tmpb}% \expandafter\edef\csname pgfutil@tmpd@\pgfutil@tmpa\endcsname{\pgfmathresult}% \expandafter\edef\csname pgfutil@tmpf@\pgfutil@tmpa\endcsname{\pgfutil@tmpa}% }% \edef\pgfutil@tmps{\pgfkeysvalueof{/list management/sort comparison}}% \pgfutil@loop \edef\pgfutil@tmpi{1}% \pgfutil@for\pgfutil@tmpb:={\pgfutil@tmph}\do{% \edef\pgfutil@tmpx{\@nameuse{pgfutil@tmpd@\pgfutil@tmpb}}% \edef\pgfutil@tmpc{\the\numexpr\pgfutil@tmpb-1}% \edef\pgfutil@tmpy{\@nameuse{pgfutil@tmpd@\pgfutil@tmpc}}% \edef\pgfutil@tmpr{(\pgfutil@tmpx\pgfutil@tmps\pgfutil@tmpy?1:0)}% \pgfmathtruncatemacro\pgfutil@tmpj{\pgfutil@tmpr}% \ifnum\pgfutil@tmpj=0\relax \edef\pgfutil@tmpi{0}% \edef\pgfutil@tmpu{\@nameuse{pgfutil@tmpf@\pgfutil@tmpb}}% \edef\pgfutil@tmpv{\@nameuse{pgfutil@tmpf@\pgfutil@tmpc}}% %\typeout{swap (\pgfutil@tmpu<->\pgfutil@tmpv) and (\pgfutil@tmpx<->\pgfutil@tmpy)}% \expandafter\edef\csname pgfutil@tmpd@\pgfutil@tmpb\endcsname{\pgfutil@tmpy}% \expandafter\edef\csname pgfutil@tmpd@\pgfutil@tmpc\endcsname{\pgfutil@tmpx}% \expandafter\edef\csname pgfutil@tmpf@\pgfutil@tmpb\endcsname{\pgfutil@tmpv}% \expandafter\edef\csname pgfutil@tmpf@\pgfutil@tmpc\endcsname{\pgfutil@tmpu}% \fi }% \ifnum\pgfutil@tmpi=0\relax \pgfutil@repeat \edef\pgfutil@tmpa{\@nameuse{pgfutil@tmpf@1}}% \edef\pgfutil@tmpc{\@nameuse{pgfutil@tmpd@1}}% \pgfutil@for\pgfutil@tmpb:={\pgfutil@tmph}\do{% \edef\pgfutil@tmpa{\pgfutil@tmpa,\@nameuse{pgfutil@tmpf@\pgfutil@tmpb}}% \edef\pgfutil@tmpc{\pgfutil@tmpc,\@nameuse{pgfutil@tmpd@\pgfutil@tmpb}}}% \edef\pgfutil@tmpb{\noexpand\edef\noexpand#1{\pgfutil@tmpc}\noexpand\edef\noexpand#2{\pgfutil@tmpa}}% \pgfmathsmuggle\pgfutil@tmpb\endgroup\pgfutil@tmpb}, /list management/.cd,print item/.code={#1}, print item in list/.code={% \if@pgf@list@first@item \pgfkeys{/list management/print item={#1}}% \@pgf@list@first@itemfalse \else \pgfkeys{/list management/print item={% \pgfkeysvalueof{/list management/list separator}#1}}% \fi}, list separator/.initial={,\space}, step count/.code={\advance\c@pgf@counta by1\relax}, sort comparison/.initial={>=}, sort map/.code={\pgfmathparse{#1}}, current array/.initial={}, store array entry/.code={\advance\c@pgf@counta by1\relax \pgfkeyssetevalue{\pgfkeysvalueof{/list management/current array}/\the\c@pgf@counta}{#1}} }% \makeatother \begin{document} \pgfkeys{/my lists/.cd, tikzlings/.initial={anteater,bear,bee,cat,coati,hippo,koala,marmot,mouse}} The first \pgfkeys{/my lists/tikzlings/.count=\myn}\myn\ Ti\emph{k}Zlings are \{\pgfkeys{/my lists/tikzlings/.print list}\}. \medskip \pgfkeys{/my lists/tikzlings/.add item=wolpertinger} After the wolpertinger joined, they were \pgfkeys{/my lists/tikzlings/.count=\myn}\myn, and the list became \{\pgfkeys{/my lists/tikzlings/.print list}\}. \medskip \pgfkeys{/my lists/tikzlings/.remove item=mouse} Sadly, the mouse could not stand the wolpertinger, so the list Ti\emph{k}Zlings got reduced to \pgfkeys{/my lists/tikzlings/.count=\myn}\myn, consisting of \{\pgfkeys{/my lists/tikzlings/.print list}\}. \bigskip \pgfkeys{/my lists/.cd, my initial array/.is array={12,19,1,3,17,4,23,5,9,7}, my values/.initial=\pgfkeysvalueof{/my lists/my initial array/content},% my values/.sort numeric list={\temp}{\templ},% sort yields sorted list and index my sorted array/.is array/.expanded={\temp}, my index machinery/.is array/.expanded={\templ}}% Often one is given a list of entries where one wants to sort on the basis of some function that can be fed with an entry. One may also want to have an index mapping which indicates at which position the $i^\mathrm{th}$ entry of the initial list ended up getting. In this example, we start out with \[\{\lambda_i\}_{i=1}^n=\{\pgfkeys{/my lists/my initial array/content/.print list}\}\;.\] The fact that we used the \texttt{/.is array} key handler means that we know its dimension, $n=\pgfkeysvalueof{/my lists/my initial array/dim}$. After sorting we get \[\{\lambda_i'\}_{i=1}^n=\{\pgfkeys{/my lists/my sorted array/content/.print list}\}\] and an index or reshuffling or permutation list \[P=(\pgfkeys{/my lists/my index machinery/content/.print list})\;.\] For instance, the $5^\mathrm{th}$ entry of the permutation list being $P_5=\pgfkeysvalueof{/my lists/my index machinery/5}$ tells us that the $\pgfkeysvalueof{/my lists/my index machinery/5}^\mathrm{th}$ entry of our original list, $\lambda_{\pgfkeysvalueof{/my lists/my index machinery/5}}=\pgfkeysvalueof{/my lists/my initial array/\pgfkeysvalueof{/my lists/my index machinery/5}}$, ended up at position $5$ in the sorted list, i.e.\ $\lambda_5'=\lambda_{\pgfkeysvalueof{/my lists/my index machinery/5}}$. Similarly, $P_3=\pgfkeysvalueof{/my lists/my index machinery/3}$ means that for the $\pgfkeysvalueof{/my lists/my index machinery/3}^\mathrm{th}$ entry of our original list, $\lambda_{\pgfkeysvalueof{/my lists/my index machinery/3}}=\pgfkeysvalueof{/my lists/my initial array/\pgfkeysvalueof{/my lists/my index machinery/3}} =\lambda_3'$. % we can't accidentally overwrite arrays \pgfkeys{/my lists/.cd,test array/.is array={a,6,pft,gamma}}% \end{document} ``` ![Screen Shot 2020-12-25 at 11.52.11 AM.png](/image?hash=4a57b838780912a93861d0ec897b9b2da02b4dfc5e8303d88b66c034ba4492c7)