Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"For in" loop equivalent in SAS 9.3

Tags:

sas

sas-macro

I'm searching for a while an equivalent of the for in loop (like in Python or in R) in SAS 9.3 macro language. The DO loop seem's to be the solution but did't work exactly as I want. I founded a way to do it in a data step with a DO loop but it don't work with the macro language. For example, in a data step, this code is working :

DATA _NULL_;
  DO i = 1,3,5,9;
    PUT i;
  END;
RUN;

And then the log prompt as expected :

1
3
5
9

When I try to do the same with an %DO loop in a Macro, I have an error.

%MACRO test();
  %DO i = 1,2,4,9 ;
    %PUT i = &i;
  %END;
%MEND;

%test();

The log promp these messages :

ERROR: Expected %TO not found in %DO statement.
ERROR: A dummy macro will be compiled

I'm quite new in SAS and stackoverflow so I hope my question is no too stupid. It's so simple to do this in Python and R then it must have a simple way to do it in SAS.

Thank's for help - J. Muller

like image 494
jomuller Avatar asked Mar 22 '13 14:03

jomuller


2 Answers

The closest I've ever come across to this pattern in SAS macro language is this:

%MACRO test();

%let j=1;
%let vals=1 2 4 9;
%do %while(%scan(&vals,&j) ne );
  %let i=%scan(&vals, &j);

  %put &i;

  %let j=%eval(&j+1);
%end;
%MEND;

%test();

(Warning: untested, as I no longer have a SAS installation I can test this out on.)

like image 113
Simon Nickerson Avatar answered Sep 19 '22 13:09

Simon Nickerson


You can certainly get around it this way:

options mindelimiter=,;
options minoperator;
%MACRO test();
  %DO i = 1 %to 9 ;
    %if &i in (1,2,4,9) %then %do;
    %PUT i = &i;
  %END;
  %end;
%MEND;

%test();

However, I think you can usually avoid this sort of call by executing your macro multiple times rather than attempting to control the loop inside the macro. For example, imagine a dataset and a macro:

data have;
input x;
datalines;
1
2
4
9
;;;;
run;

%macro test(x);
%put &x;
%mend test;

Now you want to call %test() once for each value in that list. Okay, easy to do.

proc sql;
select cats('%test(',x,')') into :testcall separated by ' ' from have;
quit;

&testcall;

That works just as well as your %do in loop, except it's data driven, meaning if you want to change the calls you just change the dataset (or if your data changes, the call automatically changes!). In general, SAS is more effective when designed as data driven programming rather than as entirely written code.

like image 32
Joe Avatar answered Sep 21 '22 13:09

Joe