4

If I have a list \def\Main{25, 76,38, 19, 58,29,88,44, 22, 11, 34, 17}
that contains some number which are inside 1,2,...,25 (inclusive).

How can I get a list which contains all numbers 1,2,...,25 except the numbers in the main list?

That means: 1,2,3,4,5,6,7,8,9,10, 12,13,14,15,16, 18,20,21, 23,24
---> I need this list for later usage!

enter image description here

\documentclass[a4paper]{article}
\usepackage{tikz}
\begin{document}
\def\Main{25,   76,38,  19,  58,29,88,44,  22,  11,  34,  17}

\let\Sub=\empty
\foreach \k in \Main{
\pgfmathparse{\k<=25 ? \k : "0"}
  \ifx\empty\Sub{} \xdef\Sub{\pgfmathresult}%
  \else \xdef\Sub{\Sub,\pgfmathresult}%
  \fi
}
\section{Show wrong List}
\Sub

\section{Needed List}
1,2,3,4,5,6,7,8,9,10,~~~  12,13,14,15,16,~~~  18,~~~ 20,21,~~~   23,24

(placed in gaps for better seeing)
\end{document}
3

A simpler implementation with expl3. Additional interfaces can be added for extracting items from lists or cycling through them.

\documentclass{article}
%\usepackage{xparse} % not needed with LaTeX 2020-10-01 or later

\ExplSyntaxOn

%% first an interface to manage clists

\NewDocumentCommand{\clistset}{mm}
 {
  \clist_clear_new:c { l__cislist_#1_clist }
  \clist_set:cn { l__cislist_#1_clist } { #2 }
 }

\NewDocumentCommand{\clistcomplement}{mmO{1}m}
 {% #1 = list to set, #2 = list to take from, #3 = start, #4 = end
  \clist_clear_new:c { l__cislist_#1_clist }
  \int_step_inline:nnn { #3 } { #4 }
   {
    \clist_if_in:cnF { l__cislist_#2_clist } { ##1 }
     {% if the current number doesn't belong to the second list, add to the first
      \clist_put_right:cn { l__cislist_#1_clist } { ##1 }
     }
   }
 }

\NewDocumentCommand{\clistuse}{mm}
 {% #1 = list, #2 = separator
  \clist_use:cn { l__cislist_#1_clist } { #2 }
 }

\ExplSyntaxOff

\begin{document}

\clistset{first}{25,   76,38,  19,  58,29,88,44,  22,  11,  34,  17}

\clistcomplement{second}{first}{25}

\clistcomplement{third}{first}[18]{36}

\clistuse{second}{, }

\clistuse{third}{--}

\end{document}

The \clistcomplement command has an optional third argument to set the starting point (default 1).

enter image description here

| improve this answer | |
2

The following provides a macro named \getmissingelements.

\getmissingelements will take two mandatory arguments followed by one optional argument specifying the number range for your list (defaulting to 1-25).

The two mandatory arguments are the macro in which the result should be stored and the main list.

It also has a starred variant which will expand the list once (so you can use \Main without the need to \expandafter it).

It works by first setting a sequence to hold the provided list, then steps through the integer range and checks whether the integer is in the sequence. If it isn't that integer gets added to a temporary comma separated list. After the stepping through the integers the user provided macro will be set to the contents of the comma separated list. All except the final assignment to the user specified macro is done locally in a group.

\documentclass[]{article}

\ExplSyntaxOn
\NewDocumentCommand \getmissingelements { s m m >{\SplitArgument{1}{-}}O{1-25} }
  {
    \IfBooleanTF {#1}
      { \cis_get_missing_elements:Nnno }
      { \cis_get_missing_elements:Nnnn }
      #2 #4 {#3}
  }
\msg_new:nnn { cis } { missing-value } { Missing~value.~Aborting! }
\cs_new_protected:Npn \cis_get_missing_elements:Nnnn #1#2#3#4
  {
    \tl_if_novalue:nTF {#3}
      { \msg_error:nn { cis } { missing-value } }
      {
        \group_begin:
          \seq_clear:N \l_tmpa_seq
          \clist_clear:N \l_tmpa_clist
          \seq_set_from_clist:Nn \l_tmpa_seq {#4}
          \int_step_inline:nnn {#2} {#3}
            {
              \seq_if_in:NnF \l_tmpa_seq {##1}
                {
                  \clist_put_right:Nn \l_tmpa_clist {##1}
                }
            }
          \exp_args:NNNV
        \group_end:
        \cs_set:Npn #1 \l_tmpa_clist
      }
  }
\cs_generate_variant:Nn \cis_get_missing_elements:Nnnn { Nnno }
\ExplSyntaxOff

\def\Main{0,1,2,3,4,5,  10,11,  20,21}

\begin{document}
\getmissingelements\cistmp{25,   76,38,  19,  58,29,88,44,  22,  11,  34,  17}
\cistmp

\getmissingelements*\cistmp{\Main}
\cistmp

% braces around `-2' to hide the minus from the argument splitting
\getmissingelements*\cistmp{\Main}[{-2}-7]
\cistmp
\end{document}

enter image description here

| improve this answer | |
2

After \Main is defined, I create 25 macros \z1, \z2, ... containing the numbers 1, 2, ...

Then I read \Main into a array list \mynums.

Then I set up a loop, and for each item in the \mynums array, if it is less than 26, I redefine the corresponding \z? value to nothing. So, for example, if the \mynums array value were 11, I redefine \z11 to nothing.

Then I cycle through the revised list of \z1, \z2, ... and concatenate each value to a string \newlist, adding a comma between list items, as needed.

The final result, therefore, resides in \newlist.

EDITED to put it all into a macro \getmissingnumbers.

\documentclass{article}
\usepackage{listofitems,pgffor}
\makeatletter
\newcommand\getmissingnumbers[1]{%
 \foreach\z in{1,...,25}{%
   \expandafter\gdef\csname z\z\expandafter\endcsname\expandafter{\z}%
 }%
 \readlist*\mynums{#1}%
 \foreachitem\z\in\mynums[]{%
   \ifnum\z<26\relax
      \expandafter\gdef\csname z\z\endcsname{}%
   \fi
 }%
 \gdef\newlist{}%
 \foreach\z in{1,...,25}{%
   \if\relax\csname z\z\endcsname\relax\else
     \if\relax\newlist\relax\else\g@addto@macro\newlist{,}\fi
     \expandafter\g@addto@macro\expandafter\newlist\expandafter{%
       \csname z\z\endcsname}%
   \fi
 }%
}
\makeatother
\begin{document}
\def\Main{25,   76,38,  19,  58,29,88,44,  22,  11,  34,  17}
\getmissingnumbers{\Main}
\newlist
\end{document}

enter image description here

| improve this answer | |
2

Also just for completion, a solution using R with knitr:

mwe

\documentclass{article}
\begin{document}
<<echo=F>>=
data <- c(14,23,7,8,9,10,1,3,4,5,6,12,13,15,16,18,20,2,21,24)
sortdata <- sort(data)
x <- split(which(data==11), cumsum(c(1, diff(which(data==11)) != 1)))
@ 
\section{Show wrong List}\Sexpr{data}
\section{Needed List}\Sexpr{sortdata}
\section{For better reading}
<<echo=F>>=
x <- split(sortdata, cumsum(c(1, diff(sortdata) != 1)))
@
There are \Sexpr{length(names(x))} consecutive intervals:
\begin{itemize}
<<results='asis',echo=F>>=
for(i in seq_along(x)) {cat("\\item");cat(x[[i]])} 
@
\end{itemize}
\end{document}
| improve this answer | |
1

Just for completion, a LuaLaTeX solution:

\sortandremove will take three arguments: the first two are the limits (order doesn't care as Lua handles with this), the third one is the list to use as filter.

%!TEX program = lualatex
\documentclass{standalone}
\usepackage{luacode}
\begin{luacode*}
userdata = userdata or {}
function userdata.sort_and_remove(a, b, ...)
    local max = (a > b and a) or b
    local min = (a > b and b) or a
    --ConTeXt goodies
    local t = table.sorted({...})
    local hash = table.tohash(t)
    --
    local result = {}
    for i = min, max do 
        if not hash[i] then result[#result +1] = i end 
    end
    return table.concat(result, ", ")
end
\end{luacode*}
\newcommand\sortandremove[3]{\directlua{tex.sprint(userdata.sort_and_remove(#1, #2, #3))}}
\begin{document}
%try \sortandremove{25}{1}{...}
\sortandremove{1}{25}{25,   76,38,  19,  58,29,88,44,  22,  11,  34,  17}
\end{document}

enter image description here

| improve this answer | |
1

Just pgf.

\documentclass{article}
\usepackage{pgf}
\makeatletter
\pgfmathdeclarefunction{MemberQ}{2}{%
  \begingroup%
    \edef\pgfutil@tmpa{\noexpand\in@{,#2,}{,#1,}}%
    \pgfutil@tmpa
    \ifin@%
      \edef\pgfmathresult{1}%
    \else
      \edef\pgfmathresult{0}%
    \fi
    \pgfmath@smuggleone\pgfmathresult%
  \endgroup}

\pgfmathdeclarefunction{Complement}{2}{%
  \begingroup%
    \edef\pgfutil@tmpb{#2}%
    \edef\pgfutil@tmpc{#1}%
    \pgfutil@for\pgfutil@tmpa:={\pgfutil@tmpb}\do{%
      \edef\pgfutil@tmpd{\noexpand\in@{,\pgfutil@tmpa,}{,\pgfutil@tmpc,}}%
      \pgfutil@tmpd
      \ifin@%
        \edef\pgfutil@tmph{\noexpand\@removeelement{\pgfutil@tmpa}{\pgfutil@tmpc}{\noexpand\pgfutil@tmpc}}%
        \pgfutil@tmph
      \fi
      }%
    \edef\pgfmathresult{\pgfutil@tmpc}% 
    \pgfmath@smuggleone\pgfmathresult% 
  \endgroup}  
\makeatother

\parindent0pt
\begin{document}
Is $1\in\{1,2,3\}$? 
\pgfmathparse{(MemberQ("{1,2,3}",1)? "yes" : "no")}\pgfmathresult.

Is $4\in\{1,2,3\}$? 
\pgfmathparse{(MemberQ("{1,2,3}",4)? "yes" : "no")}\pgfmathresult.

% just for fun: build the list from 1,...,25 with a \loop
\edef\mylist{1}%
\edef\myi{1}%
\loop
\edef\myi{\the\numexpr\myi+1}
\edef\mylist{\mylist,\myi}%
\ifnum\myi<25\repeat
\edef\Main{25,76,38,19,58,29,88,44,22,11,34,17}%
Original list: $\{\mylist\}$.

Complement: 
\pgfmathparse{Complement("\mylist","\Main")}$\{\pgfmathresult\}$.
\end{document}
| improve this answer | |
  • Puh, I wish there where sth. like \foreach \n in {...} and not in {...} {.......} – cis Oct 29 at 3:43
  • @cis This is the purpose of MemberQ. \foreach \x in {1,...,25} {\pgfmathtruncatemacro{\itest}{MemberQ("\Main",\x)} \ifnum\itest=0 $\x$ is in $\{1,\dots,25\}\setminus\mbox{Main}$.\par \fi} – user227820 Oct 29 at 3:51
1

Just because it's possible, the following is a fully expandable implementation of this using expl3. Both \cisgetmissingelements and \cisgetmissingelementsO will expand in exactly two steps of expansion and are safe inside an \edef context.

\cisgetmissingelementsO will expand the first token in the list once. Both variants take three arguments, the first two are the range and the last the list which acts as the filter.

Note that this expandable implementation does have to iterate through the filter-list for each number in the specified range (breaking the inner loop if it finds a matching element in that list), so it is comparatively slow.

\documentclass[]{article}

\ExplSyntaxOn
\scan_new:N \s__cis_mark
\cs_new:Npn \__cis_get_missing_elements_result:n #1
  {
    \tl_if_empty:nTF {#1}
      { \exp_end: }
      { \use_i:nn \exp_end: #1 }
  }
\cs_new:Npn \cis_get_missing_elements:nnn
  { \exp:w \cis_get_missing_elements_aux:nnn }
\cs_new:Npn \cis_get_missing_elements:nno
  { \exp:w \cis_get_missing_elements_aux:nno }
\cs_new:Npn \cis_get_missing_elements_aux:nnn #1#2#3
  {
    \int_step_function:nnN {#1} {#2} \__cis_get_missing_elements:nw
    \use_none:nn \s__cis_mark {#3}
    \__cis_get_missing_elements_result:n {}
  }
\cs_generate_variant:Nn \cis_get_missing_elements_aux:nnn { nno }
\cs_new:Npn \__cis_get_missing_elements:nw #1 #2 \s__cis_mark #3 
  {
    \clist_map_function:nN {#3} \__cis_get_missing_elements_aux:nw
    \prg_do_nothing: \__cis_get_missing_elements_output:nw {#1}
    #2 \s__cis_mark {#3}
  }
\cs_new:Npn \__cis_get_missing_elements_aux:nw
    #1 #2 \__cis_get_missing_elements_output:nw #3
  {
    \int_compare:nNnF {#1} = {#3}
      { #2 \__cis_get_missing_elements_output:nw {#3} }
  }
\cs_new:Npn \__cis_get_missing_elements_output:nw
    #1 #2 \__cis_get_missing_elements_result:n #3
  { #2 \__cis_get_missing_elements_result:n { #3 , #1 } }

\cs_new_eq:NN \cisgetmissingelements  \cis_get_missing_elements:nnn
\cs_new_eq:NN \cisgetmissingelementsO \cis_get_missing_elements:nno
\ExplSyntaxOff

\def\Main{0,1,2,3,4,5,  10,11,  20,21}

\begin{document}
\edef\cistmp{\cisgetmissingelementsO{1}{25}\Main}
\texttt{\meaning\cistmp}

\edef\cistmp{\cisgetmissingelementsO{-2}{7}\Main}
\texttt{\meaning\cistmp}

\edef\cistmp{\cisgetmissingelementsO{0}{5}\Main}
\texttt{\meaning\cistmp}

\edef\cistmp
  {%
    % this will expand \cisgetmissingelements exactly twice and not further
    \unexpanded\expandafter\expandafter\expandafter
      {\cisgetmissingelements{0}{6}{1,2,3,4,5}}%
  }
\texttt{\meaning\cistmp}
\end{document}

enter image description here

| improve this answer | |
1

With foreach and newif:

1. Answer:

enter image description here

\documentclass[a4paper]{article}
\usepackage{tikz}
\begin{document}
\def\Main{25,   76,38,  19,  58,29,88,44,  22,  11,  34,  17}

\section{Main List}
\Main

\section{Exclude List}
\let\Exclude=\empty
\foreach \k in \Main{
\pgfmathparse{\k<=25 ? \k : 0}
  \ifx\empty\Exclude{} \xdef\Exclude{\pgfmathresult}%
  \else \xdef\Exclude{\Exclude,\pgfmathresult}%
  \fi
}
\Exclude

\section{Target List}
\let\Target=\empty
\newif\ifexcludeelem
\excludeelemfalse
% 
\foreach \i in {1,...,25}{%%
    \excludeelemtrue
    \foreach \j in \Exclude{%
     \ifx\i\j \global\excludeelemfalse\fi
     }%
\ifexcludeelem  %Show: \i\par
  % Target List
  \ifx\empty\Target{} \xdef\Target{\i}%
  \else \xdef\Target{\Target,\i}%
  \fi
%  \else Show: {\i} is not in the list. \par 
\fi}%%

\Target
\end{document}

2. Answer:

If you have two lists, say

\def\Main{1,2,...,25}
\def\Exclude{1, 2, 3, 15, 17, 25}

and you want to build the complements or difference set:

enter image description here

\documentclass[a4paper]{article}
\usepackage{tikz}
\begin{document}
\def\Main{1,2,...,25}
\def\Exclude{1, 2, 3, 15, 17, 25}

\section{Main List}
\Main

\section{Exclude List}
\Exclude

\section{Target List}
\let\Target=\empty
\newif\ifexcludeelem
\excludeelemfalse
% 
\foreach \i in \Main{%%
    \excludeelemtrue
    \foreach \j in \Exclude{%
     \ifx\i\j \global\excludeelemfalse\fi
     }%
\ifexcludeelem  %Show: \i\par
  % Target List
  \ifx\empty\Target{} \xdef\Target{\i}%
  \else \xdef\Target{\Target, \i}%
  \fi
%  \else Show: {\i} is not in the list. \par 
\fi}%%
| improve this answer | |

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.