Hi Leonid,

Thank you for your useful and informative post. I use a similar, but different, approach in my own code. I hope you don’t mind if I share that approach.

I have a number of SAS utility macros at https://github.com/scottbass/SAS/tree/master/Macro, one of which is %loop: https://github.com/scottbass/SAS/blob/master/Macro/loop.sas.

%loop is a “wrapper” macro, which loops over a delimited list of items, tokenizing that list into individual “words”, and calling a user-defined inner macro for each “word” in the list.

Extracting the looping and parsing logic into this utility macro has allowed me to “think” differently/more clearly when approaching coding issues as you’ve described in your post. I just have to think about 1) what is the list I want to loop over, and 2) what code do I want to run for each item. For the inner macro, I know to just reference the macro variable &word at some point.

Because this is “pure macro”, i.e. executes to completion during code compilation, the approach is the same whether called in global code, data step, procs, etc.

One caveat is that, in *very* rare cases, creating the __LIST__ parameter can overflow the maximum size of a macro variable (IIRC it’s 64K bytes). In that scenario, I would use the “macro array” approach you’ve illustrated (&mvar1, &mvar2, &mvar3, etc, then referencing those variables via &&mvar&i). I may write a %loop2 version using this approach in the future.

You can simulate a multidimensional array using two (or more) delimiters. A simplistic example:

proc sql noprint;
select catx(“|”,name,sex,age) into :list separated by "^" from sashelp.class;
quit;
%macro code;
%let name=%scan(&word,1,|);
%let sex=%scan(&word,2,|);
%let age=%scan(&word,3,|);
%put &=name &=sex &=age;
%mend;
%loop(&list,dlm=^);

One last example: rename each variable in a dataset (real world example, used to load a hash object):

data work.foo;
set sashelp.shoes;
run;
proc contents data=work.foo out=contents noprint;
run;
proc sql noprint;
select name into :vars separated by " " from contents;
drop table contents;
quit;
%macro code;
&word=_&word <<< no trailing semi-colon
%mend;
proc datasets lib=work nowarn nolist;
modify foo;
rename %loop(&vars);
quit;

I use my %loop macro all the time in my own code; %loop and %seplist are probably my two most used utility macros.

I hope some of your readers may find this approach useful.

Kind Regards,
Scott