BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
hhchenfx
Barite | Level 11

Hi Everyone,

 

I try to optimize my Macro program and here is my situation.

 

I have 2 DO-LOOP (DO a, DO h).

And I have a file contains all no checking condition (no_run file).

 

So at the beginning of the inner loop (DO h), I want to check if that “h” belong to the No-Checking condition or not. If belong, move to next iteration.

 

In the example, I dont want to run the main code for Height = 56 57 58 60.

(Clearly, for the simple problem below, I can simply delete the condition from the list of h)

 


*Create a No_run file to save value;
data no_run;
input height_no_run_value;
datalines;
56
57
58
60
;run;


%Macro skip;
	%let age_list	 = 12 13 ;
	%let Height_list = 50 51 53 55 56 57 58 60;

	%do a=1 	%to %sysfunc(countw(&age_list));
	%do H=1 	%to %sysfunc(countw(&Height_list));

		%put &h;


		/*Check the no-run value*/
			%if %scan(&height_list, &h) belong to no_run file %then %GOTO EXIT_STEP;


		/*MAIN CODE*/
		data Age&a._Height&H ; set have;
		if age=%scan(&age_list, &a) and height<%scan(&height_list, &h);run;

	%end;
	%EXIT_STEP:
	%end;
%Mend;

%SKIP;

 

KEY


data have; set sashelp.class; run;

*Create a No_run file to save value;
data no_run;
input height_no_run_value;
datalines;
56
57
58
60
;run;

proc sql;
select distinct height_no_run_value into : skip_list separated by ' ' from no_run;
quit;

%put &skip_list;run;


%Macro skip;
	%let age_list	 = 12 13 ;
	%let Height_list = 50 51 53 55 			56 57 58 59 60;

	%do a=1 	%to %sysfunc(countw(&age_list));
	%do H=1 	%to %sysfunc(countw(&Height_list));

		%put %scan(&height_list, &h);


		/*Check the no-run value*/

			%let current_height = %scan(&height_list, &h);

			%if %index(&skip_list, &current_height) = 0 %then %do;


		/*MAIN CODE*/
		data Age%scan(&age_list, &a)_Height%scan(&height_list, &h) ; set have;
		if age=%scan(&age_list, &a) and height<%scan(&height_list, &h);run;

		%end;

	%end;
	%EXIT_STEP:
	%end;
%Mend;

%SKIP;
1 ACCEPTED SOLUTION

Accepted Solutions
Astounding
PROC Star

First, before calling the macro convert the list of values to skip to a single macro variable:

 

proc sql;

select distinct height_no_run_value into : skip_list separated by ' ' from no_run;

quit;

 

Then modify the macro definition to compare a single height value to the list of values in &SKIP_LIST.

 

As a general principle (and for this particular case as well), it would be a good idea to eliminate GOTOs.  That's easy enough to do here:

 

%Macro skip;
%let age_list = 12 13 ;
%let Height_list = 50 51 53 55 56 57 58 60;

%do a=1 %to %sysfunc(countw(&age_list));
%do H=1 %to %sysfunc(countw(&Height_list));

%put &h;

   /*Check the no-run value*/

   %if %index(&skip_list, %scan(&height_list, &h)) = 0 %then %do;

      /*MAIN CODE*/
      data Age&a._Height&H ; set have;
      if age=%scan(&age_list, &a) and height<%scan(&height_list, &h);run;


%end;

%end;
%end;


%Mend;

The %INDEX functions returns 0 when the second string does not appear within the first string.

 

If you had extreme values within &HEIGHT_LIST, additional care would have to be taken.  For example, "5" does actually appear within "57" and the %INDEX function would not return 0 under those conditions.  However, it seems likely that all your heights will be two digits long, and this complication will not be an issue.

View solution in original post

9 REPLIES 9
Astounding
PROC Star

First, before calling the macro convert the list of values to skip to a single macro variable:

 

proc sql;

select distinct height_no_run_value into : skip_list separated by ' ' from no_run;

quit;

 

Then modify the macro definition to compare a single height value to the list of values in &SKIP_LIST.

 

As a general principle (and for this particular case as well), it would be a good idea to eliminate GOTOs.  That's easy enough to do here:

 

%Macro skip;
%let age_list = 12 13 ;
%let Height_list = 50 51 53 55 56 57 58 60;

%do a=1 %to %sysfunc(countw(&age_list));
%do H=1 %to %sysfunc(countw(&Height_list));

%put &h;

   /*Check the no-run value*/

   %if %index(&skip_list, %scan(&height_list, &h)) = 0 %then %do;

      /*MAIN CODE*/
      data Age&a._Height&H ; set have;
      if age=%scan(&age_list, &a) and height<%scan(&height_list, &h);run;


%end;

%end;
%end;


%Mend;

The %INDEX functions returns 0 when the second string does not appear within the first string.

 

If you had extreme values within &HEIGHT_LIST, additional care would have to be taken.  For example, "5" does actually appear within "57" and the %INDEX function would not return 0 under those conditions.  However, it seems likely that all your heights will be two digits long, and this complication will not be an issue.

hhchenfx
Barite | Level 11

Thanks a lot.

Turn into a string and index it is very interesting.

 

In my actual works, I do have the "extreme" value since the list run from 0-100.

 

Can you suggest me that additional treatment?

 

HHCFX

Satish_Parida
Lapis Lazuli | Level 10
%Macro skip;
	%let age_list	 = 12 13 ;
	%let Height_list = 50 51 53 55 56 57 58 60;

	%do a=1 	%to %sysfunc(countw(&age_list));
	%do H=1 	%to %sysfunc(countw(&Height_list));

		%put &h;


		/*Check the no-run value*/
		%let x=;
		data _null_;
		x=findw(&skip_list,%scan(&height_list, &h));
		call symput('x',x);
		run;
		%if &x. = 0 %then %do;

			/*MAIN CODE*/
			data Age&a._Height&H ; set have;
			if age=%scan(&age_list, &a) and height<%scan(&height_list, &h);run;
		%end;

	%end;
	%end;
%Mend;

%SKIP;

A few modifications to @Astounding code.
Hope this helps.

Astounding
PROC Star

With extreme values, one way is to pad both %INDEX strings with blanks.  Before vs. after:

 

%if %index(&skip_list, %scan(&height_list, &h)) = 0 %then %do;

 

%if %index(%str( &skip_list ), %str( %scan(&height_list, &h) )) = 0 %then %do;

hhchenfx
Barite | Level 11

Hi,

In your code, the error show up

ERROR: Macro function %SCAN has too few arguments.

 

In Satish_Parida, the error notice is:

388: LINE and COLUMN cannot be determined.
NOTE: NOSPOOL is on. Rerunning with OPTION SPOOL might allow recovery of the LINE and COLUMN
where the error has occurred.
ERROR 388-185: Expecting an arithmetic operator.
200: LINE and COLUMN cannot be determined.
NOTE: NOSPOOL is on. Rerunning with OPTION SPOOL might allow recovery of the LINE and COLUMN
where the error has occurred.
ERROR 200-322: The symbol is not recognized and will be ignored.

 

Even when I separate it, still that error.

		%let x=1;
		%let h=2;
		data _null_;
		height_list="1 2 3 4 5 6 7";
		skip_list="6 5 8";

		x=findw(&skip_list,%scan(height_list, &h));
		call symput('x',x);
		run;
Astounding
PROC Star

OK, let's split it out as two statements, then.

 

Now:

 

%if %index(%str( &skip_list ), %str( %scan(&height_list, &h) )) = 0 %then %do;

 

The replacement (be sure to keep all those blanks inside the %INDEX function):

 

%let one_age = %scan(&height_list, &h);

%if %index(%str( &skip_list ), %str( &one_age )) = 0 %then %do;

hhchenfx
Barite | Level 11

Oh, it works perfectly now.

By the way, why %GOTO is not prefered in Macro. I see it quite easy to control the flow of code.

Thank you so much.

HHCFX

Astounding
PROC Star

Most programmers frown upon use of GOTO unless absolutely necessary.  As a general rule (and you could certainly argue that this particular case does not conform to the general rule), GOTO makes it difficult to follow the progression of the program as it moves through various processing steps.  It's easier to follow a program that moves from top to bottom, without the possibility of having to return to a prior step.

 

That being said, programming style is up to you.  It's really a question of what you find easy to read.

hhchenfx
Barite | Level 11

Thanks for the tips.

I think if the code is team-based, GOTO will drive people crazy, but it is ok if you are the only one who write and run code.

HHC

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 9 replies
  • 10656 views
  • 3 likes
  • 3 in conversation