DO Loops

DO loops can be used to execute a set of code over a series of items. These can be used in both DATA steps and open code inside of a %macro, however, we tend to use them most often in open code.

The basic DO loop syntax is

do i = /* start */ to /* end */ (by /* by value */);
    /* SAS statements */
end;

The default value of the counter is 1 but you can modify this with the by option. Also, the index value i can be called whatever you want (i.e. opportunity would also work). This is similar to a forval loop in Stata.

data out.A;
    do i = 1 to 5;
        y = i*2;
        output;
    end;
run;
i y
0 1.0 2.0
1 2.0 4.0
2 3.0 6.0
3 4.0 8.0
4 5.0 10.0
data out.A;
    do i = 1 to 100 by 20;
        y = i*2;
        output;
    end;
run;
i y
0 1.0 2.0
1 21.0 42.0
2 41.0 82.0
3 61.0 122.0
4 81.0 162.0

Or in open code

%macro doloop;
    %do i = 1 %to 5;
        %put &i;
    %end;
%mend doloop;

%doloop;
686  %doloop;
1
2
3
4
5
%macro doloop;
    %do i = 1 %to 100 %by 20;
        %put &i;
    %end;
%mend doloop;

%doloop;
699  %doloop;
1
21
41
61
81

DO-WHILE Loop

Sometimes it is useful to execute a set of commands while a condition is true. In these cases, we can use a DO-WHILE loop.

do while (cond);
    /* SAS statements */
end;

The condition is evaluated at the top of the loop before statements in the DO loop are executed. This can be used in DATA steps or open code in a %macro. For the remainder of this page, we will only show examples in open code but syntax is very similar for DATA step.

%macro dowhile(end);
    %let i = 1;
    %do %while(&i < &end);
        %put &i;
        %let i = %sysfunc(sum(&i, 1));
    %end;
%mend dowhile;

%dowhile(2);
%dowhile(5);
762  %dowhile(2);
1
763  %dowhile(5);
1
2
3
4

Iterate Over a List with DO Loop

If we want to execute some code over a list of values then we need to be a little clever. To do this, we’re going to use a few helpful SAS functions. The code to loop over a list of items is below. The basic idea is we use a DO-WHILE loop to iteratively retrieve each element of our list until we reach the end of the list.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
%macro listloop(list);

    %let i = 1;
    
    %do %while (%scan(&list, &i, " ") ~= );
        * Get the value in the ith spot;
        %let value = %scan(&list, &i, " ");
        
        * Print to log;
        %if %length(&value) ~= 0 %then %put &value;
        
        *Increment counter;
        %let i = %eval(&i + 1);
        
    %end;
    
%mend listloop;

Let’s discuss the helper functions we use in this code block. The first new function here is %scan(list, i, delim) which returns the ith item in the specified list that is delimited by delim (specifying the delimiter is optional). We use this in line 5 and 7

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
%macro listloop(list);

    %let i = 1;
    
    %do %while (%scan(&list, &i, " ") ~= );
        * Get the value in the ith spot;
        %let value = %scan(&list, &i, " ");
        
        * Print to log;
        %if %length(&value) ~= 0 %then %put &value;
        
        *Increment counter;
        %let i = %eval(&i + 1);
        
    %end;
    
%mend listloop;

We use %scan to parse through the elements of the list we are trying to loop over. We use this with a DO-WHILE loop to continue scanning elements from our list until the element we scan is empty i.e. we reached the end of the list. We use %scan again in line 7 to retrieve the ith element in our list.

Another new function we use is %length which returns the length of the given argument. We use this in line 10

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
%macro listloop(list);

    %let i = 1;
    
    %do %while (%scan(&list, &i, " ") ~= );
        * Get the value in the ith spot;
        %let value = %scan(&list, &i, " ");
        
        * Print to log;
        %if %length(&value) ~= 0 %then %put &value;
        
        *Increment counter;
        %let i = %eval(&i + 1);
        
    %end;
    
%mend listloop;

Here we use %length to check that the element we retrieved from our list is not empty i.e. has non-zero length. This is overkill since we already checked in line 5 that the element is not empty.

The last new function we use here is %eval which allows us to evaluate expressions in open code. We use this in line 13

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
%macro listloop(list);

    %let i = 1;
    
    %do %while (%scan(&list, &i, " ") ~= );
        * Get the value in the ith spot;
        %let value = %scan(&list, &i, " ");
        
        * Print to log;
        %if %length(&value) ~= 0 %then %put &value;
        
        *Increment counter;
        %let i = %eval(&i + 1);
        
    %end;
    
%mend listloop;

We use %eval to increment our counter. Recall that in a previous section we used %sysfunc combined with sum to increment our counter.