3

I have a list of 2-character strings that is read from table, e.g.

\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

I want to create a string that will contain unique 2-character strings. The result should be

SLSKMCAAPTUP

Here is what I've come up so far.

\documentclass[10pt,a4paper]{article}
\usepackage{xparse}
\usepackage{xstring}
\usepackage{filecontents}
\usepackage{pgfplotstable}
\usepackage{ifthen}

\def\mystring{} % initialize
\def\extendmystring#1#2{\edef\mystring{#1\mystring#2}}

\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

\begin{document}

\mystring{}

\pgfplotstableread[columns={Attribute}]{attributetools.dat}\datatableA
\pgfplotstablegetrowsof{\datatableA} 
\pgfmathtruncatemacro{\RowsInTable}{\pgfplotsretval-1} 
 \foreach \k in {0,...,\RowsInTable}{
 \pgfplotstablegetelem{\k}{Attribute}\of{\datatableA}
 \def\tempstring{\pgfplotsretval}
\extendmystring{}{\tempstring}
}

Display concatenated string
\mystring


\end{document}

but mystring value is empty after each iteration.

  • 1
    \foreach executes the loop code inside two groups, so assignments have to either escape that group or be global. The way your \extendmystring macro is designed you can prefix it with \global (ie, \global\extendmystring{}{\tempstring}) and it will work. – Phelype Oleinik May 5 at 22:47
  • Thanks, it works :) – Almic7 May 5 at 23:00
2

With expl3:

\begin{filecontents*}{\jobname.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\makestring}{om}
 {% #1 = macro to store the string in, #2 = file name
  \almic_makestring:n { #2 }
  \IfNoValueTF { #1 }
   { \str_use:N \l__almic_makestring_str }
   { \str_new:N #1 \str_set_eq:NN #1 \l__almic_makestring_str }
 }

\ior_new:N \g__almic_makestring_stream
\str_new:N \l__almic_makestring_str
\seq_new:N \l__almic_makestring_seq

\cs_new_protected:Nn \almic_makestring:n
 {
  % clear the auxiliary sequence
  \seq_clear:N \l__almic_makestring_seq
  % read the file and store the items
  \ior_open:Nn \g__almic_makestring_stream { #1 }
  \ior_str_map_inline:Nn \g__almic_makestring_stream
   {
    \seq_put_right:Nn \l__almic_makestring_seq { ##1 }
   }
  \ior_close:N \g__almic_makestring_stream
  % remove the first item
  \seq_pop_left:NN \l__almic_makestring_seq \l_tmpa_tl
  % remove the duplicates
  \seq_remove_duplicates:N \l__almic_makestring_seq
  %
  \str_set:Nx \l__almic_makestring_str { \seq_use:Nn \l__almic_makestring_seq {} }
 }

\ExplSyntaxOff

\begin{document}

\makestring{\jobname.dat}

\makestring[\test]{\jobname.dat}

\texttt{\meaning\test}

\end{document}

enter image description here

A “classical” implementation:

\begin{filecontents*}{\jobname.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

\documentclass{article}

\makeatletter
\newread\almic@read
\newcommand{\makestring}[2][]{% #1 = macro to store the string in, #2 = file name
  \begingroup\endlinechar=-1
  \def\almic@out{#1}%
  \let\almic@temp\@empty
  \openin\almic@read=#2\relax
  \ifeof\almic@read\else
    \read\almic@read to \almic@line % throw away the first line
    \expandafter\almic@make
  \fi
}
\newcommand{\almic@make}{%
  \loop\unless\ifeof\almic@read
    \read\almic@read to \almic@line
    \ifcsname almic@\almic@line\endcsname\else
      \expandafter\let\csname almic@\almic@line\endcsname\@empty
      \edef\almic@temp{\almic@temp\almic@line}%
    \fi
  \repeat
  \ifx\almic@out\@empty
    \almic@temp\expandafter\endgroup
  \else
    \edef\x{\endgroup\noexpand\newcommand*\expandafter\noexpand\almic@out{\almic@temp}}%
    \expandafter\x
  \fi
}
\makeatother

\begin{document}

\makestring{\jobname.dat}

\makestring[\test]{\jobname.dat}

\texttt{\meaning\test}

\end{document}

With -shell-escape and Unix tools:

\begin{filecontents*}{\jobname.dat}
Attribute
SL
SK
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

\documentclass{article}
\usepackage{catchfile}

\makeatletter
\newcommand{\makestring}[2][]{%
  \CatchFileDef\almic@temp{|"tail -n +2 #2 | awk '!x[$0]++' | tr -d '\string\n'"}{}%
  \if\relax\detokenize{#1}\relax
    \almic@temp
  \else
    \@ifdefinable{#1}{\let#1\almic@temp}%
  \fi
}
\makeatother

\begin{document}

\makestring{\jobname.dat}

\makestring[\test]{\jobname.dat}
\texttt{\meaning\test}

\end{document}

See https://stackoverflow.com/a/11532197/923955 for awk and https://unix.stackexchange.com/a/180902 for tr.

2

To achieve this, you do not need anything beyond pgfplotstable.

\documentclass[10pt,a4paper]{article}
\usepackage{pgfplotstable}

\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

\begin{document}

\pgfplotstableread[columns={Attribute}]{attributetools.dat}\datatableA
\pgfplotstablegetrowsof{\datatableA} 
\pgfmathtruncatemacro{\RowsInTable}{\pgfplotsretval-1} 
\foreach \k in {0,...,\RowsInTable}{
 \pgfplotstablegetelem{\k}{Attribute}\of{\datatableA}
 \ifnum\k=0
   \xdef\myLst{\pgfplotsretval}
   \xdef\mystring{\pgfplotsretval}
 \else
   \xdef\tmpElOld{0}
   \edef\newelement{\pgfplotsretval}
   \foreach \oldelement in \myLst
   {\ifx\newelement\oldelement
     % \typeout{duplicate}
      \xdef\tmpElOld{1}
     \fi}
   \ifnum\tmpElOld=0
     \xdef\myLst{\myLst,\pgfplotsretval}
     \xdef\mystring{\mystring\pgfplotsretval}
   \fi
 \fi 
}
\mystring

\end{document}

enter image description here

Or with the aid of this answer

\documentclass[10pt,a4paper]{article}
\usepackage{pgfplotstable}

\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}
\makeatletter% from https://tex.stackexchange.com/a/260921/121799
\newcommand*{\IfStringInList}[2]{%
  \in@{,#1,}{,#2,}%
  \ifin@
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\makeatother

\begin{document}

\pgfplotstableread[columns={Attribute}]{attributetools.dat}\datatableA
\pgfplotstablegetrowsof{\datatableA} 
\pgfmathtruncatemacro{\RowsInTable}{\pgfplotsretval-1} 
\foreach \k in {0,...,\RowsInTable}{
 \pgfplotstablegetelem{\k}{Attribute}\of{\datatableA}
 \ifnum\k=0
   \xdef\myLst{\pgfplotsretval}
   \xdef\mystring{\pgfplotsretval}
 \else
   \xdef\tmpElOld{0}
   \edef\newelement{\pgfplotsretval}
   \edef\tmp{\noexpand\IfStringInList{\pgfplotsretval}{\myLst}{%
   %\noexpand\typeout{duplicate-1}%
   }{%
   \noexpand\xdef\noexpand\myLst{\myLst,\pgfplotsretval}%
   \noexpand\xdef\noexpand\mystring{\mystring\pgfplotsretval}}}
   \tmp
 \fi 
}
\mystring\myLst

\end{document}
1

A classical implementation is possible in case the 2-character-strings do neither contain unbalanced curly braces ({ and/or } nor contain hashes (#) nor contain percents (%) that should be visible.

Be a ware that classical TeX's \read-routine does read a single line only in case this is possible without the tokens obtained from that containing unbalanced/unmatched curly braces. Otherwise it reads as many lines as necessary for obtaining a set of tokens without unbalanced/unmatched curly braces.

Therefore in the example below I implemented things in a way where you can easily switch to having things read under verbatim-catcode-régime in case you prefer that.

\documentclass{article}

\begin{filecontents*}{attributetools.dat}
Attribute
SL
SK 
MC
SL
AA
PT
SK
UP
MC
\end{filecontents*}

\makeatletter
%%-----------------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@tempa{}%
\newcommand\UD@tempb{}%
\newread\UD@read
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%
%% !!! The argument of \UD@ExtractFirstArg must not be empty. !!!
%% You can check for emptiness via \UD@CheckWhetherNull before applying
%% \UD@ExtractFirstArg.
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%   The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%%   <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
% \UD@GatherIfNotAlreadyPresentLoop{<write-handle>}%
%                                  {<action to perform to collection without braces when loop is finished>}%
%                                  {<strings collected so far, each string nested in braces>}%
%                                  {<strings collected/concatenated so far, each string not nested in braces>}%
%%.............................................................................
\newcommand\UD@GatherIfNotAlreadyPresentLoop[4]{%
   \ifeof#1%
     \expandafter\UD@firstoftwo
   \else
     \expandafter\UD@secondoftwo
   \fi
   {#2{#4}}%
   {%
     \immediate\read#1 to\UD@tempa
     \UD@CheckWhetherTempaInListLoop{#3}{%
        \UD@exchange{{#3}{#4}}%
     }{%
       \expandafter\UD@exchange\expandafter{%
         \romannumeral0%
         \UD@exchange{ }%
                     {\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter}%
         \expandafter\UD@exchange\expandafter{\expandafter{\romannumeral0%
            \expandafter\UD@exchange\expandafter{\UD@tempa}{ #4}}}%
         {\expandafter{\romannumeral0\expandafter\UD@exchange\expandafter{\expandafter{\UD@tempa}}{ #3}}}%
       }%
     }%
     {\UD@GatherIfNotAlreadyPresentLoop{#1}{#2}}%
   }%
}%
% \UD@CheckWhetherTempaInListLoop{<list>}
\newcommand\UD@CheckWhetherTempaInListLoop[1]{%
  \UD@CheckWhetherNull{#1}{\UD@secondoftwo}{%
    \expandafter\expandafter\expandafter\def
    \expandafter\expandafter\expandafter\UD@tempb\UD@ExtractFirstArg{#1}%
    \ifx\UD@tempa\UD@tempb
      \expandafter\UD@firstoftwo
    \else
      \expandafter\UD@secondoftwo
    \fi
    {\UD@firstoftwo}%
    {\expandafter\UD@CheckWhetherTempaInListLoop\expandafter{\UD@firstoftwo{}#1}}%
  }%
}%
%%-----------------------------------------------------------------------------
%%
\immediate\openin\UD@read attributetools.dat %
\begingroup
\endlinechar=-1 %
\UD@exchange{%
  % read away the first line:
  \ifeof\UD@read\else\immediate\read\UD@read to\UD@tempa\fi
  % do the loop:
  \UD@GatherIfNotAlreadyPresentLoop{\UD@read}%
                                   {\endgroup\newcommand\mystring}%
                                   {}%
                                   {}%
}{%
  % In case you wish to have things read from file under verbatim-catcode-régime,
  % uncomment the following three lines:
  %\let\do\@makeother
  %\dospecials
  %\do\^^I% <- verbatimize horizontal tabs also.
}%
\immediate\closein\UD@read
\makeatother


\begin{document}

\show\mystring

\texttt{\string\mystring: \meaning\mystring}

\mystring

\end{document}

enter image description here

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.