BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
Mluna
Fluorite | Level 6

Hello, I'm trying to create a macro that removes certain values from a comma-delimited list, based on a macro variable list. Here is the code I'm using with the Log. Any help would be greatly appreciated!

/*Test Table */
Data VarRemovalTbl;
input Fruits $50.;
datalines;
Apples, Oranges, Lettuce,
Oranges, Honey, Grapes,
Lettuce, Oranges,
Oranges,
Lettuce,
Honey,
;
run;
/*Macro to remove specific values from a comma-delimited list*/
%macro remove_values(data, var, remove_list);
 
/* Temporary array to store the values to remove */
data &data. (drop=i);
set &data.;
length new_&var. $255.;
array values_to_remove[99] $20 _temporary_;
 
/* populate the array with the values to remove */
do i = 1 to countw("&remove_list.",",");
values_to_remove[i] = strip(scan("&remove_list.", i, ","));
end;
 
/* Initialize the new variable */
new_&var. = "";
 
/* Loop through the values in the original variable*/
do i = 1 to countw(&var., ",");
value = strip(scan(&var., i, ","));
 
/*Check if value is in removal list */
if not(value in (of values_to_remove[*]) ) then do;
/* if not found, add it to the new variable */
if new_&var. ne "" then new_&var. = catx(", ", new_&var., value);
else new_&var. = value;
end;
end;
 
/* if the new variable is empty, set it to missing */
if new_&var. = "" then new_&var. = "";
 
/*replace the original variable with the cleaned version */
drop &var.;
rename new_&var.=&var.;
run;
%mend remove_values;
 
/* list of values to remove */
%let remove_list = Lettuce, Honey;
 
/* Call the macro to clean the 'Fruits' Variables in the dataset */
%remove_values(VarRemovalTbl, Fruits, &remove_list.);
 
proc print data=work.VarRemovalTbl;
run;
 
 
Log:

1 The SAS System 07:59 Monday, July 1, 2024

1 ;*';*";*/;quit;run;

10
11 ODS _ALL_ CLOSE;
12 OPTIONS DEV=SVG;
13 GOPTIONS XPIXELS=0 YPIXELS=0;
14 %macro HTML5AccessibleGraphSupported;
15 %if %_SAS_VERCOMP_FV(9,4,4, 0,0,0) >= 0 %then ACCESSIBLE_GRAPH;
16 %mend;
17 FILENAME EGHTML TEMP;
18 ODS HTML5(ID=EGHTML) FILE=EGHTML
19 OPTIONS(BITMAP_MODE='INLINE')
20 %HTML5AccessibleGraphSupported
21 ENCODING='utf-8'
22 STYLE=HTMLBlue
23 NOGTITLE
24 NOGFOOTNOTE
25 GPATH=&sasworklocation
26 ;
NOTE: Writing HTML5(EGHTML) Body file: EGHTML
27
28 /*Test Table */
29 Data VarRemovalTbl;
30 input Fruits $50.;
31 datalines;

NOTE: The data set WORK.VARREMOVALTBL has 6 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 0.02 seconds
cpu time 0.01 seconds

38 ;
39 run;
40 /*Macro to remove specific values from a comma-delimited list*/
41 %macro remove_values(data, var, remove_list);
42
43 /* Temporary array to store the values to remove */
44 data &data. (drop=i);
45 set &data.;
46 length new_&var. $255.;
47 array values_to_remove[99] $20 _temporary_;
48
49 /* populate the array with the values to remove */
50 do i = 1 to countw("&remove_list.",",");
51 values_to_remove[i] = strip(scan("&remove_list.", i, ","));
52 end;
53
54 /* Initialize the new variable */
55 new_&var. = "";
56
2 The SAS System 07:59 Monday, July 1, 2024

57 /* Loop through the values in the original variable*/
58 do i = 1 to countw(&var., ",");
59 value = strip(scan(&var., i, ","));
60
61 /*Check if value is in removal list */
62 if not(value in (of values_to_remove[*]) ) then do;
63 /* if not found, add it to the new variable */
64 if new_&var. ne "" then new_&var. = catx(", ", new_&var., value);
65 else new_&var. = value;
66 end;
67 end;
68
69 /* if the new variable is empty, set it to missing */
70 if new_&var. = "" then new_&var. = "";
71
72 /*replace the original variable with the cleaned version */
73 drop &var.;
74 rename new_&var.=&var.;
75 run;
76 %mend remove_values;
77
78 /* list of values to remove */
79 %let remove_list = Lettuce, Honey;
80
81 /* Call the macro to clean the 'Fruits' Variables in the dataset */
82 %remove_values(VarRemovalTbl, Fruits, &remove_list.);
ERROR: More positional parameters found than defined.
83
84 proc print data=work.VarRemovalTbl;
85 run;

NOTE: There were 6 observations read from the data set WORK.VARREMOVALTBL.
NOTE: PROCEDURE PRINT used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds

86
87 %LET _CLIENTTASKLABEL=;
88 %LET _CLIENTPROCESSFLOWNAME=;
89 %LET _CLIENTPROJECTPATH=;
90 %LET _CLIENTPROJECTPATHHOST=;
91 %LET _CLIENTPROJECTNAME=;
92 %LET _SASPROGRAMFILE=;
93 %LET _SASPROGRAMFILEHOST=;
94
95 ;*';*";*/;quit;run;
96 ODS _ALL_ CLOSE;
97
98
99 QUIT; RUN;
100

 
1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

This is the important part of your code:

%let remove_list = Lettuce, Honey;
%remove_values(VarRemovalTbl, Fruits, &remove_list.);

First the reference to REMOVE_LIST is replaced by its value.  So you made this macro call:

%remove_values(VarRemovalTbl, Fruits, Lettuce, Honey);

You defined the macro to take only three parameters.  Then you called it with four.  Don't do that.

 

If you want to pass a comma as part of the value of an argument you need to quote them.  You could use macro quoting and then your macro definition would not need to be changed.  

So either add the macro quoting when defining the macro variable:

%let remove_list = %str(Lettuce, Honey);

Or when calling the macro:

%remove_values(VarRemovalTbl, Fruits, %quote(&remove_list.));

You could also just add actual quotes or parentheses around the value.

%remove_values(VarRemovalTbl, Fruits, "&remove_list.");
%remove_values(VarRemovalTbl, Fruits, (&remove_list.));

But that would require changing the way the macro uses the parameter value to account for the extra characters it will have.

 

In the long run it will be easier to modify the macro to expect some other delimiter.

%let remove_list = Lettuce|Honey;
%remove_values(VarRemovalTbl, Fruits, &remove_list.);

View solution in original post

5 REPLIES 5
sbxkoenk
SAS Super FREQ
/* Macro to remove specific values from a comma-delimited list */
%macro remove_values(data, var, remove_list);
... ... ... ...
%mend  remove_values;
 
/* list of values to remove */
%let remove_list = Lettuce, Honey;
 
/* Call the macro to clean the 'Fruits' Variables in the dataset */
%remove_values(VarRemovalTbl, Fruits, &remove_list.);

Macro expects 3 positional parameters, but sees 4 due to comma in "Lettuce, Honey".

Try to work with another delimiter (like | ) for this parameter or use keyword parameters instead of positional parameters.

 

Koen

Tom
Super User Tom
Super User

@sbxkoenk wrote:

 

or use keyword parameters instead of positional parameters.

That will not solve the issue.  That would just result in a call like this:

%remove_values(VarRemovalTbl, Fruits, remove_list=Lettuce, Honey)

Which will not only have more parameter values passed than the macro expects, it will also have parameter value (Honey) being passed by position after another parameter value (Lettuce) that is being passed by name, which is not allowed.  All values passed by position have to come before any values passed by name.

sbxkoenk
SAS Super FREQ

@Tom ... you are absolutely right. thanks for correcting me!

Koen

Astounding
PROC Star

You're confusing macro language because commas serve two different purposes in your application.  Commas separate items in &remove_list, and they also separate parameters being passed to the macro.  How should SAS parse out your request when a macro with only three parameters resolves to:

 

%remove_values (VarRemovalTbl, Fruits, Lettuce, Honey)

 

A workaround would use spaces instead of commas:

 

%let remove_list = Lettuce Honey;

 

You would have add some programming to pick out the individual items from the removal list.  If you have two word items, you would have to use a different delimiter:

 

%let remove_list = Iceberg Lettuce ! Honey;

 

Then rely on the ! delimiter in your programming to separate items.

Tom
Super User Tom
Super User

This is the important part of your code:

%let remove_list = Lettuce, Honey;
%remove_values(VarRemovalTbl, Fruits, &remove_list.);

First the reference to REMOVE_LIST is replaced by its value.  So you made this macro call:

%remove_values(VarRemovalTbl, Fruits, Lettuce, Honey);

You defined the macro to take only three parameters.  Then you called it with four.  Don't do that.

 

If you want to pass a comma as part of the value of an argument you need to quote them.  You could use macro quoting and then your macro definition would not need to be changed.  

So either add the macro quoting when defining the macro variable:

%let remove_list = %str(Lettuce, Honey);

Or when calling the macro:

%remove_values(VarRemovalTbl, Fruits, %quote(&remove_list.));

You could also just add actual quotes or parentheses around the value.

%remove_values(VarRemovalTbl, Fruits, "&remove_list.");
%remove_values(VarRemovalTbl, Fruits, (&remove_list.));

But that would require changing the way the macro uses the parameter value to account for the extra characters it will have.

 

In the long run it will be easier to modify the macro to expect some other delimiter.

%let remove_list = Lettuce|Honey;
%remove_values(VarRemovalTbl, Fruits, &remove_list.);

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!

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
  • 5 replies
  • 364 views
  • 4 likes
  • 4 in conversation