## Loops

Super Contributor
Posts: 712

# Loops

Hi All,

the following code works.
data temp;
do i= 1 to 5 ,7,9; (Non continuous loop)
m=i+1;
end;
run;

if I use the same logic in macros it fails

%macro temp1;
data temp;
%do i= 1 %to 5,7,9;
m=&i+1;
%end;
run;
%mend temp1;
%temp1;

What could be the reason?

SASPHILE!
Occasional Contributor
Posts: 6

## Re: Loops

Hi Phille,

I read some papers about the %do statement and I didn´t find anything about this way of using %do statement with specific cases. Maybe, %do in macro function don´t have this functionallity. When I tried to run, he don´t recognize the %do parameter.

You could resolver this problem using an array to perfom the specific calculation with 5, 7 and 9.

Other think, the do statement on the sas code, execute 7 times ( he executes 1, 2, 3, 4, 5, 7, 9) and not 3 times ( using 5, 7, 9), as it appears to be created for.

Regards.

Kassim
Brazil
Frequent Contributor
Posts: 82

## Re: Loops

Try this:

%macro temp1;
data temp1;
%do i= 1 %to 9;
m=&i+1;
if &i in (1:5, 7, 9) then output;
%end;
run;
%mend temp1;
%temp1;

Maybe not the most elegant solution, but it works.

I don't know why it doesn't work in a regular way.
Frequent Contributor
Posts: 82

## Re: Loops

But maybe the answer is much simpler. In this case we don't have to use %do but just do;

%macro temp1;
data temp1;
do i= 1 to 5, 7,9;
m=i+1;
output;
end;
run;
%mend temp1;
%temp1;

It works fine. So I guess the question now is when should we use '%do' and when 'do'. Should be careful about that.
Not applicable
Posts: 0

## Re: Loops

There are two parts to this.

Firstly your non-continuous do loop is not doing what you think it is. The syntax for the do statement is:

DO index-variable=specification-1 <, . . . specification-n>;

where

specification

denotes an expression or a series of expressions in this form

start

You've given, excuse the psuedo-markup, the following:

Next, the syntax for %DO (Iterative) does not support multiple specifications and is given as

%DO macro-variable=start %TO stop <%BY increment>;

The equivalent macro code would be

%macro temp1;

data temp;

%do i= 1 %to 5;

m=&i+1;

%end;

%do i=7 %to 9 %by 2;

m=&i+1;

%end;

run;

%mend temp1;

%temp1;

which would be resolved to the following executable code:

data temp;

m=1+1;

m=2+1;

m=3+1;

m=4+1;

m=5+1;

m=7+1;

m=9+1;

run;

If you want to read the manual it is available here.

I'm still mystified by what you are trying to accomplish, but hope this helps,

ProcMe
Super Contributor
Posts: 712

## Re: Loops

Hello Procme,
There is a situation where in I have to loop through many iterations.But the case for the looping is non continous. And also the variable names are to be changed dynamically for every loop increment. Thats the reason I have to use the macro loop counter.
So the case is i will have a contimnous looping from 2 to 13 and then 17,18,21 and 29.
I was looking for a workaround in this case.
SASPHILE
Not applicable
Posts: 0

## Re: Loops

Is this the sort of thing you were after?

/*Loop rule set*/
data loops;
input from to;
infile cards missover;
cards;
1 12
17 18
21
23
;
run;
/* Play data, something to process */
data have;
do i = 1 to 50;
output;
end;
run;

%macro doloops(data=have, out=want, loopset=loops, key=i, action=output);
/*Read the loop rule set into memory */
data _null_;
set &loopset end=eof;
call symput('loop_from_' || compress(_n_), compress(from));
if to eq . then
call symput('loop_to_' || compress(_n_), compress(from));
else
call symput('loop_to_' || compress(_n_), compress(to));
if eof then
call symput('loop_count', compress(_n_));
run;
/* Process the specified dataset */
data &out(drop=_i);
set &data;
%do _loop = 1 %to &loop_count;
%let _from = &&&loop_from_&_loop;
%let _to = &&&loop_to_&_loop;
do _i = &_from to &_to;
if &key = _i then do;
&action;
end;
end;
%end;
run;

%mend doloops;

%doloops(data=have, out=want, loopset=loops, key=i, action=output);

Gives output for obs 1-12, 17-18, 21, 23.

ProcMe
Not applicable
Posts: 0

## Re: Loops

... or

data &out(drop=_i);
set &data;
do _i =
%do _loop = 1 %to &loop_count;
%let _from = &&&loop_from_&_loop;
%let _to = &&&loop_to_&_loop;

%if &_loop lt &loop_count %then &_from to &_to,
%else &_from to &_to ;
%end;
;
if &key = _i then do;
&action;
end;
end;

run;

ProcMe
Super Contributor
Posts: 712

## Re: Loops

Proc me,
Here is the case:
I would want to create datasets temp2 to temp14 and then temp16,temp17 and temp41
In the following case the loop goes through 2to41 and creates empty datasets like temp18 temp19.......... etc which i dont need.
the logic is based on the where condition and _temg001 have values only from 2 to 14 and 16,17 and 41.

%macro flatten;
%do i= 2 %to 41;
data temp&i;
set allvar.Duphic_20080110(keep= assignment_reassignment_date
HIC _TEMG001 month ma_pd_ind);
where _TEMG001=&i and ma_pd_ind='PD' and month<10;
if &i in (2:14,16,17,41) then output;
%end;
run;
%mend flatten;
%flatten;
SASPHILE
Super Contributor
Posts: 3,176

## Re: Loops

An approach I used frequently is to drive a macro execution with a DATA step and CALL EXECUTE.

An oversimplified example:

%MACRO X(VALUE);
%PUT >INFO> MACRO X INVOKED WITH VALUE: &VALUE;
%MEND X;
DATA _NULL_;
DO I=1 TO 41;
IF I IN (1,15) OR (18 LE I LE 40) THEN CONTINUE;
CALL EXECUTE('%X(' !! I !! ')');
END;
RUN;

As has been conveyed, the %IF / %END is not equal to the DO / END behavior and syntax within the SAS language, and so there are ways to work within the current system.

Scott Barry
SBBWorks, Inc.
Not applicable
Posts: 0

## Re: Loops

Would this do the trick?

data loops;
input from to;
infile cards missover;
cards;
1 12
17 18
21
23
;
run;

data have;
do _TEMG001 = 1 to 50;
output;
end;
run;

%macro doloops(data=have, out=want, loopset=loops, key=i);

data _null_;
set &loopset end=eof;
call symput('loop_from_' || compress(_n_), compress(from));
if to eq . then
call symput('loop_to_' || compress(_n_), compress(from));
else
call symput('loop_to_' || compress(_n_), compress(to));
if eof then
call symput('loop_count', compress(_n_));
run;
data
%do _loop = 1 %to &loop_count;
%do _outds = &&&loop_from_&_loop %to &&&loop_to_&_loop;
&out._&_outds.(drop=_i)
%end;
%end;
;
set &data;
do _i =
%do _loop = 1 %to &loop_count;

%if &_loop lt &loop_count %then &&&loop_from_&_loop to &&&loop_to_&_loop, ;
%else &&&loop_from_&_loop to &&&loop_to_&_loop ;

%end;
;
%do _loop = 1 %to &loop_count;
%do _outds = &&&loop_from_&_loop %to &&&loop_to_&_loop;
if &key = &_outds and _i = &_outds then output &out._&_outds;
%end;
%end;
end;

run;

%mend doloops;

%doloops(data=have, out=want, loopset=loops, key=_TEMG001);

ProcMe
Not applicable
Posts: 0

## Re: Loops

... and you can get the loops dataset using something like this:

data loop_source;
do i = 1 to 9, 12, 16 to 18, 22;
output;
end;
run;

data loops(keep=from to);
set loop_source end=eof;
retain from 0;
retain to 0;
retain last_i 0;
if _n_ = 1 then from = i;
else do;
last_i = lag(i);
if last_i ne . and last_i lt i-1 then do;
to = last_i;
output;
from = i;
end;
end;
if eof then do;
to = i;
output;
end;
run;

ProcMe
Posts: 4,736

## Re: Loops

Just two more variants how to do it:
HTH
Patrick

/* variant 1: %do %while with %scan */
%macro CreateTempDSVers1(ind=);
%let i=1;
%do %while(%scan(&ind,&i) ne );
data TempDS%scan(&ind,&i);
var='hello world';
run;
%let i=%eval(&i+1);
%end;
%mend;

%CreateTempDSVers1(ind=2 3 4 5 6 7 8 9 10 11 12 13 14 16 17 41)

/* variant 2: Call Execute */
%macro CreateTempDSVers2(ind);
data TempDS&ind;
var='hello world';
run;
%mend;

data _null_;
do i=2 to 14,16,17,41;
call execute(cats('%CreateTempDSVers2(',i,')'));
end;
run;
Super Contributor
Posts: 712

## Re: Loops

Thanks for the help.
But by invoking macros for looping causes other advanced functionality it will be 1 observation behind.
Posts: 4,736

## Re: Loops

Does this the job for you?

data have;
do i= 2 to 14,16,17,41;
_TEMG001=i; ma_pd_ind='PD'; month=7;output;
_TEMG001=i; ma_pd_ind='xx'; month=7;output;
_TEMG001=i; ma_pd_ind='PD'; month=10;output;
end;
run;

proc sql noprint;
select distinct _TEMG001, cats('work.temp',_TEMG001), cats('when(',_TEMG001,') output temp',_TEMG001,';')
into :IndList separated by ' ', utDS separated by ' ', electDs separated by ' '
from have
order by _TEMG001;
quit;
%put OutDs =%bquote(&OutDs);
%put SelectDs=%bquote(&SelectDs);

data &OutDS;
set have;
where ma_pd_ind='PD' and month<10;
select (_TEMG001);
&SelectDs
otherwise;
end;
run;
Discussion stats
• 16 replies
• 371 views
• 0 likes
• 7 in conversation