/*** HELP START ***//*
 
## >>> `%zipEvalf()` macro: <<< <a name="zipevalf-macro"></a> #######################  

The zipEvalf() and QzipEvalf() macro functions
allow to use a function on elements of pair of 
space-separated lists. 

For two space-separated lists of text strings the corresponding 
elements are taken and the macro applies a function, provided by user, 
to calculate result of the function on taken elements. 

When one of the lists is shorter then elements are "reused" starting 
from the beginning.

The zipEvalf() returns unquoted value [by %unquote()].
The QzipEvalf() returns quoted value [by %superq()].

See examples below for the details.

The `%zipEvalf()` macro executes like a pure macro code.

### SYNTAX: ###################################################################

The basic syntax is the following, the `<...>` means optional parameters:
~~~~~~~~~~~~~~~~~~~~~~~sas
%zipEvalf(
    first
   ,second
  <,function=>
  <,operator=> 
  <,argBf=>
  <,argMd=>
  <,argAf=>
  <,format=>
)
~~~~~~~~~~~~~~~~~~~~~~~

**Arguments description**:

1. `first`         - *Required*, a space-separated list of texts.

2. `second`        - *Required*, a space-separated list of texts.

* `function = cat` - *Optional*, default value is `cat`, 
                     a function which will be applied 
                     to corresponding pairs of elements of 
                     the first and the second list. 

* `operator =`     - *Optional*, default value is empty,
                     arithmetic infix operator used with elements 
                     the first and the second list. The first
                     list is used on the left side of the operator
                     the second list is used on the right side
                     of the operator. 

* `argBf =`        - *Optional*, default value is empty,
                     arguments of the function inserted
                     *before* elements the first list.
                     If multiple should be comma-separated.

* `argMd =`        - *Optional*, default value is empty,
                     arguments of the function inserted
                     *between* elements the first list and 
                     the second list.
                     If multiple should be comma-separated.

* `argAf =`        - *Optional*, default value is empty,
                     arguments of the function inserted
                     *after* elements the second list.
                     If multiple should be comma-separated.

* `format=`        - *Optional*, default value is empty,
                     indicates a format which should be used
                     to format the result, does not work when 
                     the `operator=` is used.

*//*** HELP END ***/

%macro zipEvalf(
   first
  ,second
  ,function = cat
  ,operator = 
  ,argBf=
  ,argMd=
  ,argAf=
  ,format=
)
/
des='The %zipEvalf() and %QzipEvalf() macro functions allows to use a function on elements of pair of lists.'
;
%local
  sepB sepM sepA 
  _F_ _S_ 
  NF NS  
  _Fk_ _Sk_
  _MAX_ _I_ _J_
  _RESULT_
;
%let _RESULT_ = ;

%if (%superq(first) = ) OR (%superq(second) = ) %then
  %do;
    %put NOTE: At least one of inputs is empty!;
    %put NOTE- Unchanged argument will be returned.;
    %let _RESULT_ = %superq(first)%superq(second);
    %unquote(&_RESULT_.)
    %RETURN;
  %end;

%if \%superq(argBf)\ NE \\ %then %let sepB = ,;
%if \%superq(argMd)\ NE \\ %then %let sepM = ,;
%if \%superq(argAf)\ NE \\ %then %let sepA = ,;

%let NF = %qsysfunc(countw(%superq(first ), %str( ), SQ));
%let NS = %qsysfunc(countw(%superq(second), %str( ), SQ));

%let _MAX_ = %sysfunc(max(&NF., &NS.));
/*
%LET _Fk_ = 0;
%LET _Sk_ = 0;
*/

%DO  _I_ = 1 %TO &_MAX_.;
  %if &NF. > &NS. %then
    %do;
      %if &_Sk_. = &NS. %then %let _Sk_ = 1;
                        %else %let _Sk_ = %eval(&_Sk_. + 1);
      %let _Fk_ = &_I_.; 
    %end;
  %else
    %do;
      %if &_Fk_. = &NF. %then %let _Fk_ = 1;
                        %else %let _Fk_ = %eval(&_Fk_. + 1);
      %let _Sk_ = &_I_.; 
    %end;
  
  %let _F_ = %qscan(%superq(first),  &_Fk_., %str( ), SQ);
  %let _S_ = %qscan(%superq(second), &_Sk_., %str( ), SQ);
  /*%put >>%superq(_F_)<< >>%superq(_S_)<<;*/

  %if %superq(operator)= %then
    %do;
      %let _RESULT_ = 
        %qsysfunc(strip(%superq(_RESULT_) %qsysfunc(&function.(&argBf.&sepB.
                                                                %superq(_F_)
                                                               &sepM.&argMd.
                                                               ,%superq(_S_)
                                                               &sepA.&argAf.), 
                                          &format.) 
                        ));
      /*%put >>%superq(_RESULT_)<<;*/
    %end;
  %else
    %do;
      %let _RESULT_ = 
        %qsysfunc(strip(%superq(_RESULT_) %sysevalf(%superq(_F_) &operator. %superq(_S_)) 
                        ));
    %end;

%END;

/*%superq(_RESULT_)*/
%unquote(&_RESULT_.)
%RETURN;
%mend zipEvalf;

/*** HELP START ***//*
 
### EXAMPLES AND USECASES: ####################################################

**EXAMPLE 1.** Simple concatenation of elements:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y);
%put &=x;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 2.** Shorter list is "reused":
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%let x = %zipEvalf(1 2 3 4 5 6, a b c);
%put &=x;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 3.** Use of the `operator=`, shorter list is "reused":
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%let y = %zipEvalf(1 2 3 4 5 6, 100 200, operator = +);
%put &=y;

%let z = %zipEvalf(1 2 3 4 5 6 8 9 10, 1 2 3 4 5 6 8 9 10, operator = **);
%put &=z;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 4.** Format result:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y, format=$upcase.);
%put &=x;

%put *
%zipEvalf(
 ą ż ś ź ę ć ń ó ł
,Ą Ż Ś Ź Ę Ć Ń Ó Ł
,format = $brackets.
)
*;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 5.** Use with macro variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%let abc = 10 100 1000;
%put *
%zipEvalf(
%str(1 2 3 4 5 6 7 8 9)
,&abc.
,function = sum
)
*;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 6.** If one of elements is empty:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%put *
%zipEvalf(
 abc efg
,
)
*;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 7.** Use of the `function=`, shorter list is "reused":
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%put *
%zipEvalf(
 a b c
,efg
,function = catx
,argBf = %str(,)
,format = $brackets.
)
*;

%put *
%zipEvalf(
 a b c
,efg
,function = catx
,argBf = %str( )
,format = $upcase.
)
*;

%put *
%zipEvalf(
 %str(! @ # $ [ ] % ^ & * )
,1 2 3 4 5 6 7 8 9
,function = catx
,argBf = %str( )
,format = $quote.
)
*;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 8.** Use inside resolve:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
data _null_;
z = resolve('
%zipEvalf(
 %nrstr(! @ # $ [ ] % ^ & *)
,1 2 3 4 5 6 7 8 9
,function = catx
,argBf = %str(.)
,format = $quote.
)');
put z=;
run;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 9.** Use in data step:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
data test;
  %zipEvalf(
     a b c d e f g 
    ,1 2 3 4 5 6 7
    ,function = catx
    ,argBf    = =
    ,format   = $semicolon.
  )
run;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 10.** With 9.4M6 hashing() function:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%put %zipEvalf(MD5 SHA1 SHA256 SHA384 SHA512 CRC32, abcd, function = HASHING);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 11.** Use middle argument:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
%let x = %zipEvalf(1 2 3 4 5 6, 2020, argMd=5, function=MDY, format=date11.);
%put &=x;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---

*//*** HELP END ***/

/**###################################################################**/
/*                                                                     */
/*  Copyright Bartosz Jablonski, since 2020.                           */
/*                                                                     */
/*  Code is under the MIT license. If you want - you can use it.       */
/*  But it comes with absolutely no warranty whatsoever.               */
/*  If you cause any damage or something - it will be your own fault.  */
/*  You've been warned! You are using it on your own risk.             */
/*  However, if you decide to use it don't forget to mention author.   */
/*  Bartosz Jablonski (yabwon@gmail.com)                               */
/*                                                                     */
/**###################################################################**/



/*
data _null_;
  t1 = '"f';
  t2 = "6";
  text = catx(" ", t1, t2);
  put t1= t2= text=;
run;

%let t1 = %nrstr(%"f);
%let t2 = 6;
%put *&=t1*&=t2*%qsysfunc(catx(%str( ), %superq(t1), %superq(t2)))*;

%let s1 = %str(%"f);
%let s2 = 6;
%put *&=s1*&=s2*%qsysfunc(catx(%str( ), %superq(s1), %superq(s2)))*;

%let s1 = %str(%"f);
%let s2 = 6;
%put *&=s1*&=s2*%qsysfunc(catx(%str( ), %bquote(&s1), %bquote(&s2)))*;

data _null_;
  call symputx("a1","'f");
run;
%let a2 = 6;
%put *a1=%bquote(&a1)*&=a2*%qsysfunc(catx(%str( ), %bquote(&a1), %bquote(&a2)))*;

%put *a1=%bquote(&a1)*&=a2*%sysfunc(catx(%str( ), %bquote(&a1), %bquote(&a2)))*;
*/
