4

How to generalize this macro to make it print the number of times the string occurs and allow arbitrary separator including blank space? Thanks.

Outputs could be:

0 if FALSE; 1,2,3... number of times it occurs if TRUE.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn \NewDocumentCommand{\ifmember}{mmmm} { \clist_if_in:onTF { #2 } { #1 } { #3 } { #4 } } \cs_generate_variant:Nn \clist_if_in:nnTF { o } \ExplSyntaxOff

\begin{document}

\ifmember{3}{3,4}{True}{False} (expected: True)

\ifmember{3}{ 3 ,4}{True}{False} (expected: True)

\newcommand{\foo}{3,4} \ifmember{3}{\foo}{True}{False} (expected: True)

\renewcommand{\foo}{3, 4}

\ifmember{4}{\foo}{True}{False} (expected: True)

\ifmember{foo}{\foo}{True}{False} (expected: False)

\end{document}

Krantz
  • 159

1 Answers1

1

You can map the list and check every item against the given token list, incrementing the counter if they match. At the end, you print the counter's value.

The separator (default a comma) is specified in an optional argument.

\documentclass{article}
%\usepackage{xparse} % no longer needed

\ExplSyntaxOn

\NewDocumentCommand{\countmember}{O{,}mm} { \krantz_countmember:onn { #3 } { #2 } { #1 } }

\seq_new:N \l__krantz_countmember_items_seq \int_new:N \l__krantz_countmember_count_int

\cs_new_protected:Nn \krantz_countmember:nnn { \int_zero:N \l__krantz_countmember_count_int \seq_set_split:Nnn \l__krantz_countmember_items_seq { #3 } { #1 } \seq_map_inline:Nn \l__krantz_countmember_items_seq { \tl_if_eq:nnT { ##1 } { #2 } { \int_incr:N \l__krantz_countmember_count_int } } \int_to_arabic:n { \l__krantz_countmember_count_int } } \cs_generate_variant:Nn \krantz_countmember:nnn { o }

\ExplSyntaxOff

\begin{document}

\countmember{3}{3,4} (expected: 1)

\countmember{3}{ 3 ,4} (expected: 1)

\newcommand{\foo}{3,4} \countmember{3}{\foo} (expected: 1)

\renewcommand{\foo}{3, 4, 4}

\countmember{4}{\foo} (expected: 2)

\countmember{foo}{\foo} (expected: 0)

\countmember[\]{1}{1 \ 2 \ 1 \ 0} (expected: 2)

\countmember[ ]{2}{1 2 1 0} (expected: 1)

\renewcommand{\foo}{1 2 1 0}

\countmember[ ]{2}{\foo} (expected: 1)

\end{document}

enter image description here

It is possible to add a trailing optional argument that should be a control sequence in which the result will be stored for later use.

\documentclass{article}
%\usepackage{xparse} % no longer needed

\ExplSyntaxOn

\NewDocumentCommand{\countmember}{O{,}mmo} { \krantz_countmember:onn { #3 } { #2 } { #1 } \IfNoValueTF { #4 } {% just print the count \int_to_arabic:n { \l__krantz_countmember_count_int } } {% store \tl_clear_new:N #4 \tl_set:Nx #4 { \int_to_arabic:n { \l__krantz_countmember_count_int } } } }

\seq_new:N \l__krantz_countmember_items_seq \int_new:N \l__krantz_countmember_count_int

\cs_new_protected:Nn \krantz_countmember:nnn { \int_zero:N \l__krantz_countmember_count_int \seq_set_split:Nnn \l__krantz_countmember_items_seq { #3 } { #1 } \seq_map_inline:Nn \l__krantz_countmember_items_seq { \tl_if_eq:nnT { ##1 } { #2 } { \int_incr:N \l__krantz_countmember_count_int } } } \cs_generate_variant:Nn \krantz_countmember:nnn { o }

\ExplSyntaxOff

\begin{document}

\countmember{3}{3,4} (expected: 1)

\countmember{3}{ 3 ,4} (expected: 1)

\newcommand{\foo}{3,4} \countmember{3}{\foo} (expected: 1)

\renewcommand{\foo}{3, 4, 4}

\countmember{4}{\foo} (expected: 2)

\countmember{foo}{\foo} (expected: 0)

\countmember[\]{1}{1 \ 2 \ 1 \ 0} (expected: 2)

\countmember[ ]{2}{1 2 1 0} (expected: 1)

\renewcommand{\foo}{1 2 1 0}

\countmember[ ]{2}{\foo} (expected: 1)

% store in \foo

\countmember{1}{1,2,2,1,1,2}[\foo]

\foo

\end{document}

The output is the same as before, but you will also see “3” at the end.

egreg
  • 1,121,712
  • Hi @egreg. After further testing, I found the output is not recognized as an integer. E.g., if I use \usepackage{fp} then \fpeval{\countmember[ ]{2}{\foo}}, it does not work. I want to re-utilize the number. Any thoughts? – Krantz Oct 19 '23 at 00:38
  • @Krantz I can't see how to make \countmember expandable. It's not difficult to add the possibility of storing the result so you can use it in calculations. – egreg Oct 19 '23 at 08:22
  • Hi @egreg. storing the result so [I] can use it in calculations could be great. That's what the question is about. That's why I asked the macro to output integers. Any thoughts? – Krantz Oct 19 '23 at 08:51
  • 1
    @Krantz Added as requested – egreg Oct 19 '23 at 09:00
  • Great. Well done. Thank you. Just a follow-up: can we have it count number of occurrence of ~? It is not recognized now. – Krantz Oct 19 '23 at 10:19
  • 1
    @Krantz If I do \countmember{~}{1,~,~,1,1,~}, I get 3. – egreg Oct 19 '23 at 10:39
  • Hi @egreg. Is it possible to get this to count number of single blank spaces " " in \countmember[]{}{1 ~ ~ 1 1 ~} to get 5? – Krantz Oct 22 '23 at 17:20
  • @Krantz Empty delimiter is not really supported. – egreg Oct 22 '23 at 21:25