Setting several macro variables with a loop

Reply
New Contributor
Posts: 2

Setting several macro variables with a loop

Hi there, im trying to get a loop to set several macro variables with this program:

 

%let month = 9099;
%macro Loop(several);
%let n=%sysfunc(countw(&several));
%do i=1 %to &n;
%let month_&i._F = %sysfunc(SUM(-%SYSFUNC(MDY(01,01,1993)),%SYSFUNC(INTNX(month,%SYSFUNC(sum(&month,%SYSFUNC(MDY(01,01,1993)))),-&i+1,BEGINNING)))); %put &&month_&i._F;
%let month_&i._L = %sysfunc(SUM(-%SYSFUNC(MDY(01,01,1993)),%SYSFUNC(INTNX(month,%SYSFUNC(sum(&month,%SYSFUNC(MDY(01,01,1993)))),-&i+1,END))));%put &&month_&i._L;
%end;
%mend;
%Loop(1 2);

/*CHECK*/
%put &month;
%put &month_1_F;
%put &month_2_L;


I started to read the log, and seemed to work just fine

MLOGIC(LOOP): Beginning execution.
MLOGIC(LOOP): Parameter SEVERAL has value 1 2
MLOGIC(LOOP): %LET (variable name is N)
SYMBOLGEN: Macro variable SEVERAL resolves to 1 2
SYMBOLGEN: Macro variable N resolves to 2
MLOGIC(LOOP): %DO loop beginning; index variable I; start value is 1; stop value is 2; by value is 1.
SYMBOLGEN: Macro variable I resolves to 1
MLOGIC(LOOP): %LET (variable name is MONTH_1_F)
SYMBOLGEN: Macro variable MONTH resolves to 9099
SYMBOLGEN: Macro variable I resolves to 1
MLOGIC(LOOP): %PUT &&month_&i._F
SYMBOLGEN: && resolves to &.
SYMBOLGEN: Macro variable I resolves to 1
SYMBOLGEN: Macro variable MONTH_1_F resolves to 9070 ... and so on executing the loop

but then when the check comes is like the macro variables that the loop generated doesn t exisits and I can t use them in other programs.

/*CHECK*/
32 %put &month;
SYMBOLGEN: Macro variable MONTH resolves to 9099
9099
33 %put &month_1_F;
WARNING: Apparent symbolic reference MONTH_1_F not resolved.
&month_1_F
34 %put &month_2_L;
WARNING: Apparent symbolic reference MONTH_2_L not resolved.
&month_2_L

does enyone knows why?
thanks!!
I m using SAS Guide 4.3 version.

Lucas

Super User
Posts: 12,148

Re: Setting several macro variables with a loop

The generic issue is Macro Variable Scope. Variables created in a macro only exist for the duration of the variable.

This might fix your missing values

 

%macro Loop(several);
%let n=%sysfunc(countw(&several));
%do i=1 %to &n;
%global month_&i._F month_&i._L;
%let month_&i._F = %sysfunc(SUM(-%SYSFUNC(MDY(01,01,1993)),%SYSFUNC(INTNX(month,%SYSFUNC(sum(&month,%SYSFUNC(MDY(01,01,1993)))),-&i+1,BEGINNING)))); %put &&month_&i._F;
%let month_&i._L = %sysfunc(SUM(-%SYSFUNC(MDY(01,01,1993)),%SYSFUNC(INTNX(month,%SYSFUNC(sum(&month,%SYSFUNC(MDY(01,01,1993)))),-&i+1,END))));%put &&month_&i._L;
%end;
%mend;

The %global statement should place the variables in the global macro variable table for access outside of the macro.

 

 

You might describe what that code is supposed to do as most uses of macros involving that many %sysfunc calls indicate some unneeded complexity that often maybe shouldn't be in the macro language.

New Contributor
Posts: 2

Re: Setting several macro variables with a loop

The point of this macro is to set several dates into variables.
I have the first date in the macrovariable &month, and then I do a loop to set 12 month with the first and last day of each one.
Since SAS date and my warehouse does not began at the same time, I have to use that function to calculate the difference.
Maybe an easier way exist, the difference between both starting date is 12054 days.
I used to have a function with that number, but then someone told me that this was a little better.
Sorry if my english is not too good, I tried to make myself clear.
Happy Holidays and thanks for the response, it worked just fine.
Super User
Super User
Posts: 7,405

Re: Setting several macro variables with a loop

[ Edited ]

That explanation is more about WHAT the macro is doing.  Looking at the code it appears that the macro will calculate begin and end days for multiple months relative to a given base date (stored in the macro variable MONTH).  But you are counting days since 1993 instead of days since 1960 like SAS does.

 

To get a better solution you really should explain WHY you want these new macro variables.  And why do you need to generate so many at once.

 

 

You can certainly simplify your code.  There is no need to use the MDY function to generate a constant date, just use a date literal instead. There is no need to use the %SYSFUNC() to call the SUM() function to add numbers.  The %SYSEVALF() function will let you do arithmetic on macro expressions.

 

There are also some question to the parameters to the macro. Why is the base date value stored in a global macro variable instead of being passed as a parameter to the macro?  Why is the macro parameter SEVERAL only being used to count how many values to generate?  If that is all you need then why not just pass in N as the macro parameter?  If you did want to pass a list of offsets then why not use them?

 

So here is a version of your macro that makes the above simplifications, but also adds another parameter to let the user pass in the base date if they don't want to use the macro variable MONTH as the basedate.

%let month = 9099;
%macro Loop(several,baseday);
%local i dt;
%if not %length(&baseday) %then %let baseday=&month ;
%let dt=%sysfunc(putn(&baseday+'01JAN1993'd,date9));
%do i=1 %to %sysfunc(countw(&several));
  %global month_&i._f month_&i._l ;
  %let month_&i._F = %sysevalf(%SYSFUNC(INTNX(month,&baseday+'01JAN1993'd,1-&i,B))-'01JAN1993'd); 
  %let month_&i._L = %sysevalf(%SYSFUNC(INTNX(month,&baseday+'01JAN1993'd,1-&i,E))-'01JAN1993'd);
  %put &=baseday &=dt &=i month_&i._F=&&month_&i._F month_&i._L=&&month_&i._L ;
%end;
%mend;

So if we try it with your example value of 2 months of output and a base day values of 9099 this is what we get.

549   %Loop(1 2);
BASEDAY=9099 DT=30NOV2017 I=1 month_1_F=9070 month_1_L=9099
BASEDAY=9099 DT=30NOV2017 I=2 month_2_F=9039 month_2_L=9069

So if looks like your example base date of 9,099 corresponds to the last day of November 2017.  So if we try it with a base date just a few days earlier is should generate the same macro variables, but if it is even one day later (9,100) then we should see the generated macro variables values move forward by a month.  Also since if is not using the value of SEVERAL it doesn't matter if it contains numbers, just how many words it contains.

550   %Loop(A B,baseday=9095);
BASEDAY=9095 DT=26NOV2017 I=1 month_1_F=9070 month_1_L=9099
BASEDAY=9095 DT=26NOV2017 I=2 month_2_F=9039 month_2_L=9069
551   %loop(X1 X2,baseday=9100);
BASEDAY=9100 DT=01DEC2017 I=1 month_1_F=9100 month_1_L=9130
BASEDAY=9100 DT=01DEC2017 I=2 month_2_F=9070 month_2_L=9099

 

Ask a Question
Discussion stats
  • 3 replies
  • 87 views
  • 1 like
  • 3 in conversation