Hi all,
I have a macro quoting question I hope you can help me with. I have the following code below. As you can see, I am passing a list of provider names into individual macro values. Then I am creating a dataset for each provider name by subsetting for the provider name in the second data step (macro groups).
The problem with the following statement (if &var. = %UNQUOTE(%NRSTR("&&ProvName&i..")) then output &data._&i.;) is that I have cases such as this, a provider with a name such as "UNC P&A DEPAETMENT OF ANESTHESIOLOGY". - Note the "&A" in bold above.
Therefore the code above becomes: if ProvName = "UNC P&A DEPAETMENT OF ANESTHESIOLOGY" then output prov_nomiss_3393;
but then I get the warning: Apparent symbolic reference A not resolved.
I know I need to mask these special character, but I'm getting stuck on how to do it. What I am doing below is clearly not working. How can I successfully mask these special characters and stop get these warnings?
Your help is highly appreciated. Thanks,
Alex
/*******************************************************************/
/*Passing the Provider Name into individual macro variables*/
/*******************************************************************/
data nodup_prov2;
set nodup_prov;
*ProvName2 = strip('"'||strip(ProvName)||'"'); /*Quoted Provider Name*/
cnt = strip(_n_);
/*creating the individual macros*/
call symputx ('ProvName'||strip(cnt), ProvName); /*individual macro variables for each ProvName*/
call symputx ('n', strip(cnt)); /*macro with total number of observations on ProvName*/
run;
/*Example output*/
%put &n.;
%put &ProvName1.;
%put &&ProvName&n..;
/************************************************************************************/
/* Macro below generates the new data sets. Dynamically produce data set names */
/* on the DATA statement, using the ProvName macro above to create the new data sets*/
/* based upon the value of each unique ProvName macro value. */
/************************************************************************************/
%macro groups(data,var);
data %do i=1 %to &n.;
&data._&i.
%end;;
set &data.;
%do i=1 %to &n.;
if &var. = %UNQUOTE(%NRSTR("&&ProvName&i..")) then output &data._&i.;
%end;
run;
%mend groups;
/************************************************************************/
/* Calling the macro GROUPS.
/************************************************************************/
%groups(prov_nomiss, ProvName)
**;
Try this:
if "&var" = "&&ProvName&i" then output &data._&i.;
Thanks slchen for the reply.
Unfortunately your suggestion doesn't work. This ends up quoting the variable name only, without addressing the quoted provider names, which can contain special character or mnemonics.
it resolves to: if "ProvName" = "UNC P&A DEPAETMENT OF ANESTHESIOLOGY" then output prov_nomiss_3393;
I know have to use something like either %STR, %NRSTR, %SUPERQ, or something else, to protect these special character in the provider name, but I am struggling with how.
Thanks again.
Not a solution but may complicate troubleshooting:
if &var. = %UNQUOTE(%NRSTR("&&ProvName&i..")) then output &data._&i.;
has 2 periods after the &I so the variable would resolve with an extra . at the end and fail equality.
Worth a try:
if var = "%superq(ProvName&i)" then ...
Good luck.
I think this will work! %superQ is the only one I know that does not resolve to the end of the road
Ok, besides what has suggested, a possible approach could also be:
upstream:
call symputx ('ProvName'||strip(cnt), cats('%nrstr(',ProvName,')'));
downstream:
if &var. = "&&ProvName&i.." then output &data._&i.;
You see, %UNQUOTE(%NRSTR ... will cancel each other, in your case, it is just redundant. What you need is to quote '&' at the macro compiling stage. So you need to quote it before assignment, you can't do it after that point.
Hi Hai.Ku,
Fantastic. That did it the trick!! I was trying all kinds of iterations of these masking quoting function, and I was still getting warnings of unresolved macros due to special characters. Your call symput suggestion upstream solved the problem!
Thanks Hai.Ku and all you guys that contributed to this conversation. I truly appreciate it.
Cheers!
Ok, I have a follow up question on this exercise.
We were successful in masking macro triggers, but now I am getting errors due to some provider names containing unmatched quotes. I'm getting something like the following:
WARNING: The quoted string currently being processed has become more than 262 characters long. You may have unbalanced quotation marks.
NOTE: Line generated by the macro variable "PROVNAME2473".
1 "%NRSTR(PETER B O'DONNELL, MD)
-------------
63
ERROR 63-169: The word currently being processed has exceeded the maximum length of 65535 characters.
What else can I add to the code below in order to fix this issue? Again your help is highly appreciated.
Below is the code I am using:
/*******************************************************************/
/*Passing the Procedure Description into individual macro variables*/
/*******************************************************************/
data _null_;
set nodup_prov;
cnt = strip(_n_);
/*creating the individual macros*/
/* CATS function is a concatenation (concatenates multiple strings in one function call).
It functions the same way as the CAT function, but also STRIPs leading and trailing blanks*/
call symputx ('ProvName'||strip(cnt), CATS('%NRSTR(',ProvName,')')); /*individual macro variables for each ProvName*/
call symputx ('n', strip(cnt)); /*macro with total number of observartions on ProvName*/
run;
**;
/*Example output*/
%put &n.;
%put &ProvName1.;
%put &&ProvName&n..;
/************************************************************************************/
/* Macro below generates the new data sets. Dynamically produce data set names */
/* on the DATA statement, using the ProvName macro above to create the new data sets*/
/* based upon the value of each unique ProvName macro value. */
/************************************************************************************/
%macro groups(data,var);
data %do i=1 %to &n.;
&data._&i.
%end;;
set &data.;
%do i=1 %to &n.;
if &var. = "&&ProvName&i.." then output &data._&i.;
%end;
run;
%mend groups;
/************************************************************************/
/* Calling the macro GROUPS.
/************************************************************************/
%groups(prov_nomiss, ProvName)
See suggestion #4 above.
Yeah... the problem is the complexity of the macro variable to be used with %superq.
Note that in this example the macro variable &&ProvName&i.. is not only one macro, it's many since it varies by the values of i, which takes on the value of the variable cnt, which is the number of observation in the dataset (in this example, there are about 3800 providers).
An example of using %superq would be something like the code below. As you can see, the macro variable NewTitle will take the old macro variable Title (without the &) with the function %Superq. As you can see, my case is a bit more complicated since my macro variable &&ProvName&i.. is actually about 3800 difference macro values.
DATA _NULL_;
CALL SYMPUTX('Title','Revenue: %Breakdown by Region');
RUN;
%LET NewTitle = %SUPERQ(Title);
*** Warning Given ***;
%PUT Title = &Title;
*** No warning given ***;
%PUT NewTitle = &NewTitle;
So yes, %superq is probably the way to go, the issue is the way the macro &&ProvName&i.. is set up, since I'd have to remove the &'s from &&ProvName&i.., something like %superq(ProvNamei), which SAS doesn't like.
I'm totally stuck guys. Let me know what you think.
Thank you!
Thanks Astounding for figuring this out (giving credit to where credit is due). Thanks for bouncing ideas with me guys. Attached is the solution. This will mask everything in the character string. The key is to keep the iterative i as a macro variable:
/*******************************************************************/
/*Passing the Procedure Description into individual macro variables*/
/*******************************************************************/
data _null_;
set nodup_prov;
cnt = strip(_n_);
/*creating the individual macros*/
call symputx ('ProvName'||strip(cnt), ProvName); /*individual macro variables for each ProvName*/
call symputx ('n', strip(cnt)); /*macro with total number of observartions on ProvName*/
run;
/*Example output*/
%put &n.;
%put &ProvName1.;
%put &&ProvName&n..;
/************************************************************************************/
/* Macro below generates the new data sets. Dynamically produce data set names */
/* on the DATA statement, using the ProvName macro above to create the new data sets*/
/* based upon the value of each unique ProvName macro value. */
/************************************************************************************/
%macro groups(data,var);
data %do i=1 %to &n.;
&data._&i.
%end;;
set &data.;
%do i=1 %to &n.;
if &var. = "%superq(ProvName&i)" then output &data._&i.;
%end;
run;
%mend groups;
/************************************************************************/
/* Calling the macro GROUPS.
/************************************************************************/
%groups(prov_nomiss, ProvName)
Of course %superq() works as long as you only need to resolve one layer deeper. There are many macro quoting functions, some are broad, but not too deep, such as %superq(), some are deep but not as broad in a way, such as %nrstr() that best deals with & and %, such as %nrbquote() that best deals with unbalanced ()'". In SAS there are most likely more than one way to skin a cat. If go back to my solution,
Upstream can stay the same, this tackles &,
call symputx ('ProvName'||strip(cnt), cats('%nrstr(',ProvName,')'));
Only add another quoting function to tackle unbalanced '",
if &var. = "%nrbquote(&&ProvName&i..)" then output &data._&i.;
Hi Hai.Kuo,
thanks for showing us a different way to get this done. The subject of macro quoting functions just got a lot less nebulous for me after this discussion. I really appreciate you guys' help.
Thank you, have a good day!
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.