Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Inspired from Macro to access a specific member of a list.

I want to have a macro implementing the lisp functions (member item list) and (assoc id alist). The first checks if an item is contained in a list, the second accesses the value with the given id in an associative list.

The macro should be able to take a litteral list, or a macro.

Usage should be like:

\ifassoc{42}{1,2,3}{if-found}{if-not-found}
\ifassoc{42}{\somelist}{if-found}{if-not-found}
\ifassoc{b}{a/1,b/2,c/3}{\assocresult equals 2}{if-not-found}
\ifassoc{b}{\otherlist}{\assocresult can be used here}{if-not-found}
share|improve this question
add comment

3 Answers

I hope I can state this fact here and now and give a full solution later. To illustrate my point (B) above, try:

\makeatletter
\newcount\cnta
\cnta\z@
\foreach \p/\q in {A,B}{%
  \global\advance\cnta\@ne
  \expandafter\xdef\csname result@\romannumeral\cnta\endcsname{\q}%
}
\edef\result{\result@i,\result@ii}
\show\result
\makeatother

A general scheme like the following will be preferred, but I guess there is already a package that offers this DB functionality.

\def\collections{
  1/colors:        1/red, 2/green, 3/blue;
  2/tech makers:   1/Toyota, 2/Apple, 3/Samsung, 4/Motorola, 5/Honda;
  3/IT firms:      1/Apple, 2/Microsoft, 3/Google, 4/Oracle;
  4/automakers:    1/Toyota, 2/Ford, 3/Mercedes, 4/GM, 5/Honda;
  5/units:         1/pt, 2/ex, 3/em, 4/cc, 5/in;
  6/sports:        1/basketball, 2/tennis, 3/football, 4/soccer;
  7/energy:        1/gas, 2/oil, 3/coal, 4/solar, 5/nuclear;
}

% Eg, \addrow{Continents: Africa, Asia, Europe, N.America, S.America, Ausie?}
\def\addrow#1{%

}
% Eg, \removerow{IT firms}
\def\removerow#1{%

}
% Eg, \addcol{units}{mm}
\def\addcol#1#2{%

}
% Eg, \removecol{sports}{tennis}
\def\removecol#1#2{%

}

% 1. What is the association (row) of automakers?
% 2. What is the association (column) of GM in automakers?

% Eg, \rowof{automakers}
\def\rowof#1{%

}
% Eg, \rowandcolof{automakers}{GM}
\def\rowandcolof#1#2{%

} 
share|improve this answer
add comment

As usual expl3 has easy tools for this. I hope the code is self-explanatory (well, there are comments...)

\documentclass{article}
\usepackage{xparse,expl3}

\ExplSyntaxOn
% variables:
\bool_new:N \l_georges_assoc_found_bool
\tl_new:N   \l_georges_assoc_item_tl
\tl_new:N   \l_georges_assoc_value_tl
\tl_new:N   \assocresult

% the main function:
\cs_new_protected:Npn \georges_ifassoc:nnTF #1#2#3#4
  {
    \bool_set_false:N \l_georges_assoc_found_bool
    \tl_clear:N \l_georges_assoc_value_tl
    % go through the comma list:
    \clist_map_inline:nn { #2 }
      {
        % separate item and (possible) value:
        \georges_separate_item_value:w ##1 // \q_stop
        % if we found the item stop the loop:
        \tl_if_eq:nVT { #1 } \l_georges_assoc_item_tl
          {
            \bool_set_true:N \l_georges_assoc_found_bool
            \clist_map_break:
          }
      }
    % make the value accessible:
    \tl_set_eq:NN \assocresult \l_georges_assoc_value_tl
    \bool_if:NTF \l_georges_assoc_found_bool { #3 } { #4 }
  }
\cs_generate_variant:Nn \tl_if_eq:nnT { nV }
\cs_generate_variant:Nn \georges_ifassoc:nnTF { no }

% separate item and value:    
\cs_new:Npn \georges_separate_item_value:w #1/#2/#3 \q_stop
  {
    % store item and remove trailing and leading space:
    \tl_set:Nx \l_georges_assoc_item_tl { \tl_trim_spaces:n { #1 } }
    % #3 = / if value given:
    \tl_if_eq:nnT { #3 } { / }
      { \tl_set:Nn \l_georges_assoc_value_tl { #2 } }
  }

% user command:
\NewDocumentCommand \ifassoc { mmmm }
  { \georges_ifassoc:noTF { #1 } { #2 } { #3 } { #4 } }
\ExplSyntaxOff

\begin{document}

\def\somelist{1,2,3,42}
\def\otherlist{a/1,b/2,c/3}

\ifassoc{42}{1,2,3}{if-found}{if-not-found}

\ifassoc{42}{\somelist}{if-found}{if-not-found}

\ifassoc{b}{a/1,b/2,c/3}{\assocresult\ equals 2}{if-not-found}

\ifassoc{b}{\otherlist}{\assocresult\ can be used here}{if-not-found}

\ifassoc{42}{ 42 /ab , 43/cd }{\assocresult}{F}

\ifassoc{ab}{ ab /42 , cd/43 }{\assocresult}{F}

\end{document}

enter image description here

share|improve this answer
    
I would probably not absorb #3 and #4 in \georges_ifassoc:nnTF, as they will be picked up by the final \bool_if:NTF anyway. –  Joseph Wright Sep 5 '12 at 20:33
    
Yes, that's true… When I started with \georges_ifassoc:nnTF I wasn't sure how it was going to look. They're leftovers :) –  cgnieder Sep 5 '12 at 20:36
add comment

This macro meets the spcifications:

\newcommand{\ifassoc}[4]{%
  \edef\dotheloop{%
    \noexpand\foreach \noexpand\a/\noexpand\b in {#2} {%
      \noexpand\IfEq{\noexpand\a}{#1}{%
        \noexpand\gdef\noexpand\memberresult{true}%
        \noexpand\xdef\noexpand\assocresult{\noexpand\b}%
        \noexpand\breakforeach%
      }{}%
    }%
  }%
  \xdef\assocresult{}%
  \xdef\memberresult{false}
  \dotheloop%
  \IfEq{\memberresult}{true}{#3}{#4}%
}%

MWE:

\documentclass{article}
\usepackage{tikz}
\usepackage{xstring}
\begin{document}

\newcommand{\ifassoc}[4]{%
  \edef\dotheloop{%
    \noexpand\foreach \noexpand\a/\noexpand\b in {#2} {%
      \noexpand\IfEq{\noexpand\a}{#1}{%
        \noexpand\gdef\noexpand\memberresult{true}%
        \noexpand\xdef\noexpand\assocresult{\noexpand\b}%
        \noexpand\breakforeach%
      }{}%
    }%
  }%
  \xdef\assocresult{}%
  \xdef\memberresult{false}
  \dotheloop%
  \IfEq{\memberresult}{true}{#3}{#4}%
}%

% Simple list of important numbers, given inline.
\ifassoc{2.72}{42,3.14}{
  This text will not be displayed.
}{
  Whoops, we forgot to put $e$ !
}

\ifassoc{2.72}{42,3.14,2.72}{
  $e$ is an important number. \texttt{\textbackslash assocresult=\assocresult}.
}{
  This text will not be displayed.
}

% Associative list of associative lists, given as a macro.
\def\collections{
  Colors/{1/red,2/green,3/blue},
  Foo/{1/foo,2/bar,3/baz,4/quux},
  Letters/{1/a,2/b,3/c,4/d,5/e}%
}
\ifassoc{Foo}{\collections}{
  % We can run \foreach on the result of \ifassoc.
  Found id \texttt{Foo}, associated with the pairs
  \foreach \i/\j in \assocresult {\i\ and \j, }%
  and that's all.
  % We can run \ifassoc again on the result of \ifassoc.
  \ifassoc{4}{\assocresult}{The element with id \texttt{4} is \assocresult.}{This text will not be displayed.}
  \ifassoc{5}{\assocresult}{This text will not be displayed.}{There is no element with id \texttt{5}.}
}{
  This text will not be displayed
}

\ifassoc{Digits}{\collections}{
  This text will not be displayed.
}{
  There is no collection with id \texttt{Digits}.
}

\end{document}

Output:

Output of the code above

share|improve this answer
    
(A) This solution works for \ifassoc{42}{|_|42|_|/ab|_|,|_|43/cd|_|}, but fails for, eg, \ifassoc{ab}{|_|ab|_|/42|_|,|_|cd/43|_|}, where |_| is space. The difference is in the way xstring does comparisons for numbers and plain strings. Spurious spaces may easily get into the list. Hence a robust solution should first trim the spurious spaces. Also, either slash (/) or comma (,) may uncommonly be active, making the list unparsable with \foreach. (B) If you do \foreach \a/\b in {42,3.14}{<do>}, \b will be 42 and 3.14 for the 2 items. –  Ahmed Musa Sep 5 '12 at 17:25
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.