1

I'm trying to have a command take in a comma separated list early in a document, and then later on test a given comma separated list to see if it contains (exactly) the same elements. Thus;

\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand{\activetags}{yellow, blue, red, green}
\newif\ifis@Strict
\is@Stricttrue

\newcommand{\strictTest}[1]
    {
    \renewcommand*{\do}[1]
        {
        \in@{##1}{\activetags}
            \ifin@{}% If the element is in the second string do nothing
            \else
            \is@Strictfalse% If you find a non fitting string, mark strict as false.
            \fi
        }
    }

\newcommand{\filter}[1]{
\strictTest{#1}{\activetags}%  Check one being subset of the other
\docsvlist{#1}
\strictTest{\activetags}{#1}% Check other being subset of the one
\docsvlist{\activetags}
\ifis@Strict{The elements match!}\else{The elements don't match}\fi
\is@Stricttrue
}


\begin{document}

\filter{yellow, blue, red, green}% This should show "The elements match!"
\filter{yellow, blue, red}% This should show "The elements don't match"
\filter{yellow, blue, red, green, purple}% This should show "The elements don't match"

\ifis@Strict{It works}\else{It failed}\fi% This should show "It works"

\end{document}

I can't seem to find any info on the syntax on \in@ anywhere. As I understand it, it takes 2 arguments and tests one of them to see if it is contained as a substring in the other. This seems to be usable for what I need, but I can't seem to get macros to expand inside it.

The macro seems to work if I hardwire the \activetags command, but I can't seem to get \expandafter to work correctly, nor any of the \edef or \let commands that I know of that expand the token early...

I'm probably just missing something silly, but It's been a couple hours of trying now...

Thanks

1

\in@ doesn't perform expansion on its arguments; if you want it, you have to do it yourself.

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newif\ifis@Strict
\is@Stricttrue

\newcommand{\strictTest}[1]{%
  \renewcommand*{\do}[1]{%
    \begingroup\edef\x{\endgroup\noexpand\in@{##1}{\activetags}}\x
    \ifin@
      % If the element is in the second string do nothing
    \else
      \is@Strictfalse% If you find a non fitting string, mark strict as false.
    \fi
  }%
}

\newcommand{\filter}[1]{%
  \strictTest{#1}{\activetags}%  Check one being subset of the other
  \docsvlist{#1} % here we want a space
  \strictTest{\activetags}{#1}% Check other being subset of the one
  \docsvlist{\activetags} % here we want a space
  \ifis@Strict
    The elements match!%
  \else
    The elements don't match%
  \fi
  \is@Stricttrue
}
\makeatother

\newcommand{\activetags}{yellow, blue, red, green}


\begin{document}

\filter{yellow, blue, red, green}% This should show "The elements match!"

\filter{yellow, blue, red}% This should show "The elements don't match"

\filter{yellow, blue, red, green, purple}% This should show "The elements don't match"

%\ifis@Strict{It works}\else{It failed}\fi% This should show "It works"

\end{document}

I have reformatted the code in order to avoid spurious spaces in the output.

However, it's not really clear what you want. The test with

\filter{green, yellow, blue, red}

would return false. There's no item by item comparison.


Here's an implementation with expl3 that compares both lists item by item and returns true when the items coincide even if in different order.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\clist_new:N \l_jason_active_list_clist
\bool_new:N \l_jason_strict_bool

\cs_new_protected:Nn \jason_strict_test:n
 {
  \bool_set_true:N \l_jason_strict_bool
  % check whether the input items are in the fixed list
  \clist_map_inline:nn { #1 }
   {
    \clist_if_in:NnF \l_jason_active_list_clist { ##1 }
     { \bool_set_false:N \l_jason_strict_bool }
   }
  % check whether the fixed items are in the input list
  \clist_map_inline:Nn \l_jason_active_list_clist
   {
    \clist_if_in:nnF { #1 } { ##1 }
     { \bool_set_false:N \l_jason_strict_bool }
   }
 }

\NewDocumentCommand{\setactivelist}{m}
 {
  \clist_set:Nn \l_jason_active_list_clist { #1 }
 }

\NewDocumentCommand{\filter}{mmm}
 {
  \jason_strict_test:n { #1 }
  \bool_if:NTF \l_jason_strict_bool { #2 } { #3 }
 }
\ExplSyntaxOff

\setactivelist{yellow, blue, red, green}

\begin{document}

Match: \filter{yellow, blue, red, green}{The elements match}{The elements don't match}

Match: \filter{green, yellow, blue, red}{The elements match}{The elements don't match}

Not match: \filter{yellow, blue, red}{The elements match}{The elements don't match}

Not match: \filter{yellow, blue, red, green, purple}{The elements match}{The elements don't match}

\end{document}

enter image description here

  • Ah, this looks to be what I'm after I think. I wanted to do item by item comparison. I was trying to use the docsvlist to separate the coma separated list effectively twice to do it, but I figured there was a better way. Googling hinted at exp13, but I don't know much of anything about it and couldn't find much by way of decent intro documentation. Do you have a handy reference you could point me to? The code you wrote looks like an entirely different language to me heh. Regardless, I appreciate the help! – Jason Dec 18 '16 at 14:58
  • 1
    @Jason texdoc expl3 and texdoc interface3 (you can go to texdoc.net if you can't run those commands from a terminal yourself). – egreg Dec 18 '16 at 15:01
  • That's a fantastic resource I didn't know about, added to the list heh. Followup: I was trying to lookup info on the \begingroup \endgroup. As I understand it, it's a way of forcing tex to group terms together, sort of like \csname and \endcsname, but for generic grouping and not just command names. Is this correct? – Jason Dec 18 '16 at 15:26
0

If you just want to compare two strings in an unexpandable manner, you can use the \ifx primitive with both arguments stored in temporary macros with fully expanded replacement texts:

\documentclass{article}

\newcommand{\activetags}{yellow, blue, red, green}

\makeatletter
\newcommand\filter[1]{%
    \edef\tempA@{#1}%
    \edef\tempB@{\activetags}%
    \ifx\tempA@\tempB@
        The elements match\par
    \else
        The elements don't match\par
    \fi
}
\makeatother

\begin{document}

\filter{yellow, blue, red, green}% This should show "The elements match!"
\filter{yellow, blue, red}% This should show "The elements don't match"
\filter{yellow, blue, red, green, purple}% This should show "The elements don't match"

\end{document}

Your version doesn't work, as you said, because of the expansion issue. Getting the first and second argument fully expanded is a bit trickier if you want to do it in an expandably manner.

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.