I have two strings of equal lengths, let's say abc and def (in reality they can be much longer). Both strings consist only of letters a-z, A-Z (no numbers, no special characters etc.). I need to extract out the characters individually from both strings, and use them pairwise in another macro that I have defined.

For e.g. For the two strings abc and def and some other macro \printchar[2]{#1,#2} that I have defined, I want to be able to pass the characters pair by pair into \printchar, that is,

\printchar{a}{d}
\printchar{b}{e}
\printchar{c}{f}

in perhaps, a for loop of some kind. So I guess the question is two-fold:

  1. How to extract out characters from two input strings of equal lengths, character by character, and
  2. Subsequently use them, pairwise, inside a macro. (and it should loop through all characters in the string).

Pseudo-Code:

\documentclass[]{article}
\newcommand\printchar[2]{#1,#2}
\newcommand\foo[2]{% Takes in two strings
    % <begin loop>
    % Extracts out one character from both strings
    \printchar{#1}{#2}%
    % Continue loop until strings run out of characters
    % <end loop>
}

\begin{document}
    \foo{abc}{def} % Should print a,d,b,e,f
\end{document}

From some searching around, I think maybe the xstring package can do the first part? But I'm not too sure how to integrate it with a for loop to achieve what I want.

  • Will both strings always consist of printable characters -- more precisely, characters with catcode "letter" and/or "other"? Or might the strings contain arbitrary characters, incl "special" characters such as % and {? – Mico May 2 '17 at 14:35
  • Oh yes, good point. No, they will always be letters. (a-z, A-Z). I'll update it in the question – Troy May 2 '17 at 14:36
  • Why the apparent haste to "accept" one of the answers? You can upvote good answers immediately, of course. Site guidelines suggest, though, waiting several hours and maybe even day before accepting the best answer. Hasty acceptance of an answer tends to completely discourage the posting of any further answers. – Mico May 2 '17 at 15:26
  • Oops, sorry @Mico. I will keep that in mind in the future.. :x – Troy May 2 '17 at 15:31
up vote 4 down vote accepted

Without any packages:

\documentclass[]{article}
\makeatletter
\newcommand\printchar[2]{#1,#2}
\def\parsefoo#1#2,#3#4|{%
    \ifx\relax#1\relax%
    \else%
        \ifx\relax#3\relax%
        \else%
            \printchar{#1}{#3}%
            \ifx\relax#2\relax%
            \else%
                \ifx\relax#4\relax%
                \else%
                    ,\edef\@footmp{#2,#4|}%
                    \expandafter\parsefoo\@footmp%
                \fi%
            \fi%
        \fi%
    \fi%
}
\newcommand\foo[2]{% Takes in two strings
    % <begin loop>
    % Extracts out one character from both strings
    \edef\@footmp{#1,#2|}%
    \expandafter\parsefoo\@footmp%
    %\printchar{#1}{#2}%
    % Continue loop until strings run out of characters
    % <end loop>
}
\makeatother

\begin{document}
    \foo{abc}{def} % Should print a,d,b,e,f
    haha
\end{document}

results

The loop will stop when either strings runs out of items.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\foo}{mmm}
 {% #1 is the macro to use, #2 is the first string, #3 is the second string
  \troy_foo:Nnn #1 { #2 } { #3 }
 }

\seq_new:N \l_troy_foo_first_seq
\seq_new:N \l_troy_foo_second_seq

\cs_new_protected:Nn \troy_foo:Nnn
 {
  % split the first string into items
  \seq_set_split:Nnn \l_troy_foo_first_seq { } { #2 }
  % split the second string into items
  \seq_set_split:Nnn \l_troy_foo_second_seq { } { #3 }
  % map over the two string
  \seq_mapthread_function:NNN \l_troy_foo_first_seq \l_troy_foo_second_seq #1
 }
\ExplSyntaxOff

\newcommand{\printchars}[2]{#1#2\par}

\begin{document}

\foo{\printchars}{abc}{def}

\end{document}

enter image description here

A fully expandable variant:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\loopstrings}{mmm}
 {
  \troy_loopstrings:Nnn #1 { #2 } { #3 }
 }
\cs_new:Nn \troy_loopstrings:Nnn
 {
  \__troy_loopstrings:Nffff #1
   { \tl_head:n { #2 } } { \tl_head:n { #3 } }
   { \tl_tail:n { #2 } } { \tl_tail:n { #3 } }
 }

\cs_new:Nn \__troy_loopstrings:Nnnnn
 {
  \bool_lazy_or:nnF { \tl_if_empty_p:n { #2 } } { \tl_if_empty_p:n { #3 } }
   {
    #1{#2}{#3}
    \troy_loopstrings:Nnn #1 { #4 } { #5 }
   }
 }
\cs_generate_variant:Nn \__troy_loopstrings:Nnnnn { Nffff }
\ExplSyntaxOff

\newcommand{\printchars}[2]{#1#2\par}

\begin{document}

\loopstrings{\printchars}{abcd}{1234}

\loopstrings{\printchars}{abc}{1234}

\loopstrings{\printchars}{abcd}{123}

\end{document}
  • Yay, faster than egreg :) – Skillmon May 2 '17 at 14:50
  • @Skillmon Even with longer code! – egreg May 2 '17 at 14:51
  • Just longer because of the many \ifx to end the loop if one argument runs out of tokens. – Skillmon May 2 '17 at 14:52
  • @Skillmon +1 for both of your answers! Both seem to do fine for what I'm working on in reality, but I guess I'll accept @Skillmon's no packages answer, and a reward for being faster than egreg. :) – Troy May 2 '17 at 15:10

Here's a LuaLaTeX-based solution. (Exact same output as in @egreg's answer, hence no separate screenshot.)

%% to be compiled under LuaLaTeX
\documentclass{article}
\usepackage{luacode} 
\begin{luacode}
  function string_pair ( u , v )
    for i=1,string.len(u) do 
      tex.sprint ( string.sub (u,i,i) .. string.sub (v,i,i) .. "\\par" )
    end
  end
\end{luacode}
\newcommand\foo[2]{\directlua{string_pair(\luastring{#1},\luastring{#2})}}

\begin{document}
\foo{abc}{def}
\end{document} 

When I was having fun with strings (Looping over strings) I also made a macro for looping over two strings. However, after the replies I got there it seems that it is not a good solution since it does not use tail recursion. On the other hand, it has the advantage of being easy to understand and therefore I put it here.

\documentclass{article}

\begin{document}

\def\doubleloop<#1#2><#3#4>{%
  \ifx\relax#1 \else
    \ifx\relax#3 \else
      [#1,#3] 
      \doubleloop<#2><#4>
    \fi
  \fi}

\def\loopstrings#1#2{\doubleloop<#1\relax><#2\relax>}

\verb|\loopstrings{abcd}{1234}| \loopstrings{abcd}{1234}

\verb|\loopstrings{abc}{1234}| \loopstrings{abc}{1234}

\verb|\loopstrings{abcd}{123}| \loopstrings{abcd}{123}

\end{document}

enter image description here

This can be turned into a macro using tail recursion:

\documentclass{article}

\begin{document}

\makeatletter
\def\doubleloop<#1#2><#3#4>{%
  \ifx#1\relax
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {\doubleloopaux{#1}{#2}{#3}{#4}}%
}
\def\doubleloopaux#1#2#3#4{%
  \ifx#3\relax
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {[#1,#3]\doubleloop<#2><#4>}%
}
\makeatother

\def\loopstrings#1#2{\doubleloop<#1\relax><#2\relax>}

\verb|\loopstrings{abcd}{1234}| \loopstrings{abcd}{1234}

\verb|\loopstrings{abc}{1234}| \loopstrings{abc}{1234}

\verb|\loopstrings{abcd}{123}| \loopstrings{abcd}{123}

\end{document}
  • @egreg: Thanks! I had been trying to convert it to tail recursion without success. I find this a really nice setup. If I understand it well, the \@firstofone just eliminates the braces? – Raoul Kessels May 8 '17 at 6:54
  • Yes, but they're also necessary in order for \@gobble to work, in case the firs string has run off of items – egreg May 8 '17 at 7:02

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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