%global macrovariable; %let macrovariable = Value1; %macro check_and_add(parameter_macrovariable =, parameter_value =); %if ^%index(%bquote(&&¶meter_macrovariable), ¶meter_value) %then %do; %let ¶meter_macrovariable = &&¶meter_macrovariable. ¶meter_value.; %put Updated list: ¶meter_macrovariable., new added value: ¶meter_value.; %end; %mend; %check_and_add(parameter_macrovariable = macrovariable, parameter_value = Value2); %put &=macrovariable;
Is this what you want?
The code supplied by @yabwon works well too 🙂
@
Unfortunately it does not work.
ERROR: Expecting a variable name after %let.
A bit extended version which handles empty, or not existing global macro variable you want to add values to:
%macro add_value(val,mvName=myMacrovariable)/minoperator mindelimiter=' ';
%if 0 = %SYMGLOBL(&mvName.) %then %global &mvName.;
%if %superq(&mvName.) = %then
%let &mvName. = &val.;
%else %if NOT (&val. in (&&&mvName.))
%then
%let &mvName. = &&&mvName. &val.;
%mend add_value;
/* Ecample 1 */
%global myMacrovariable;
%let myMacrovariable = A ;
%put &=myMacrovariable.;
%add_value(A)
%put &=myMacrovariable.;
%add_value(B)
%put &=myMacrovariable.;
%add_value(C)
%put &=myMacrovariable.;
%add_value(C)
%put &=myMacrovariable.;
%add_value(A)
%put &=myMacrovariable.;
/* Ecample 2 */
%global myXYZ;
%let myXYZ = ;
%put &=myXYZ.;
%add_value(A,mvName=myXYZ)
%put &=myXYZ.;
%add_value(B,mvName=myXYZ)
%put &=myXYZ.;
%add_value(C,mvName=myXYZ)
%put &=myXYZ.;
%add_value(B,mvName=myXYZ)
%put &=myXYZ.;
/* Ecample 3 */
%add_value(A,mvName=myTotalNew)
%add_value(B,mvName=myTotalNew)
%add_value(B,mvName=myTotalNew)
%add_value(C,mvName=myTotalNew)
%put &=myTotalNew.;
Bart
Note that only testing whether or not a GLOBAL macro variable with that name exists or not enough.
Consider this example:
375 %macro test; 376 %local xxx; 377 %if 0=%symglobl(xxx) %then %do; 378 %put Will try to make XXX global; 379 %global xxx; 380 %end; 381 %let xxx=From macro; 382 %put &=xxx; 383 %mend test; 384 %test; Will try to make XXX global ERROR: Attempt to %GLOBAL a name (XXX) which exists in a local environment. XXX=From macro 385 %put &=xxx; WARNING: Apparent symbolic reference XXX not resolved. xxx
To reference the value of macro variable whose name is stored in a macro variable you need to add more &.
&&&mvar
The first two resolve to one and trigger the macro processor to re-scan the results.
So your macro should look more like:
%macro check_and_add(mvar=,value=);
%if 0=%length(&mvar) %then %put ERROR: MVAR not specified.;
%else %do;
%if not %symexist(&mvar) %then %global &mvar;
%if not %sysfunc(findw(&&&mvar,&value,,s)) %then %do;
%let &mvar = &&&mvar &value ;
%put Updated list: &mvar=&&&mvar ;
%end;
%end;
%mend;
PS Using variable names that are too long is just as hard to understand as using variable names that are too short. And much harder to type.
CORRECTED: Fixed the call to FINDW() to use S modifier instead of trying to get %SYSFUNC() to pass a single space as the delimiter list.
If had already &&&:
%let &mvar = &&&mvar &value ;
%let &p_list. = &&&p_list. &p_value.;
I realy wonderig why it is working in your case?
And this is also not the goal:
%then %global &mvar;
The variable must be global outside the macro. So it can be reused.
AI is very bad in SAS 🙂
You did not use the &&& in the FINDW() function call.
The %IF %SYMEXIST()... statement is to define &MAR as global when it does not already exist. Otherwise it will be defined as local and disappear when the macro ends.
You don't need to define &MVAR as GLOBAL if it already exists.
You don't want to define &MVAR as GLOBAL if it already exists as LOCAL in some other macro that is calling this macro. SAS will generate and error.
Hence the use of the %IF statement.
The if-statement worked fine. If change &&& in the fincw() function I got an Error. The error occurs in the assigment %let =. Please concentrate on that.
"The %IF statement will only define &MAR as global when it is needed." - The variable is global and should stay global because it will be reused, e.g.:
check_and_add(mvar= mvar,value= Value1);
check_and_add(mvar= mvar,value= Value1);
check_and_add(mvar= mvar,value= Value2);
check_and_add(mvar= mvar,value= Value2);
check_and_add(mvar= mvar,value= Value3);
@obvisou wrote:
The if-statement worked fine. If change &&& in the fincw() function I got an Error. The error occurs in the assigment %let =. Please concentrate on that.
"The %IF statement will only define &MAR as global when it is needed." - The variable is global and should stay global because it will be reused, e.g.:
check_and_add(mvar= mvar,value= Value1); check_and_add(mvar= mvar,value= Value1); check_and_add(mvar= mvar,value= Value2); check_and_add(mvar= mvar,value= Value2); check_and_add(mvar= mvar,value= Value3);
Perhaps English is not you first language? The reason to have the %GLOBAL statement in the macro itself is for defensive programming. If the user of the macro passes in a macro variable name that does not exist then you cannot return a value into it. So rather than depend on the user remembering to only pass in the names of macro variables that already exist you can execute the %GLOBAL statement to define the macro variable in the global scope so you can return a value into it. But you don't want to just unconditionally execute the %GLOBAL statement in case the user wanted to use a macro variable that was LOCAL to some other macro.
Your example calls cannot work because MVAR is already defined as LOCAL because it was a PARAMETER of the macro. You could add an additional test to the macro definition.
%if 0=%length(&mvar) %then %put ERROR: Target macro variable is required.;
%else %if MVAR=%qupcase(&mvar) %then %put ERROR: Invalid macro variable name. MVAR is already used by &sysmacroname..;
%else ...
I believe here exists a missunderstanding. If the user of the macro passes in a macro variable name that does not exist, then it should crash. There is no way, even defensive to do something. Otherwise you did not understand what my goal is. Again I need "only" this:
%let parameter_macrovariable = ¶meter_macrovariable. ¶meter_value.;
But because SAS is not so flexibel I have to find an other approach, a workaround. And it must be reusable for new variables, for new values. Hope now it is clear.
You posted something again about the if statement and I have to say again that this is not an issue. For testing you can trash the hole if-statement. If will still not work!
And if would be nice if we could concentrate on the same variable and values name. Otherwise it becomes confusing and error-prone.
@obvisou wrote:
I believe here exists a missunderstanding. If the user of the macro passes in a macro variable name that does not exist, then it should crash. There is no way, even defensive to do something. Otherwise you did not understand what my goal is. Again I need "only" this:
%let parameter_macrovariable = ¶meter_macrovariable. ¶meter_value.;
But because SAS is not so flexibel I have to find an other approach, a workaround. And it must be reusable for new variables, for new values. Hope now it is clear.
You posted something again about the if statement and I have to say again that this is not an issue. For testing you can trash the hole if-statement. If will still not work!
And if would be nice if we could concentrate on the same variable and values name. Otherwise it becomes confusing and error-prone.
I don't understand what you are saying.
The line you posted is adding a value to the macro variable named parameter_macrovariable
If the name of the macro variable does not change then you are done.
If you want the name of the macro variable to user defined then you could put the NAME into a macro variable. And the use the macro variable with the NAME to generate the %LET statement.
So these two statements will generate the same %LET statement as you showed.
%let name=parameter_macrovariable ;
%let &name = &&&name ¶meter_value.;
But there is a problem the macro I posted. Not with the macro variable name references, but with it trying to use FINDW() that way with %SYSFUNC(). SAS does not like the use of single space as one of the optional arguments. It is because %SYSFUNC() is trying to figure out if it should tell FINDW you are passing a numeric value or a character value. But you can leave the delimiter parameter empty and use the S modifier instead to tell FINDW() to use space as the delimiter.
%sysfunc(findw(&&&mvar,&value,,s))
Another way to use macro logic for this is to make a macro that just returns the desired value. That would avoid any need to pass the name of a macro variable to the macro.
So something like:
%macro check_and_add(current,new);
%if %sysfunc(findw(¤t,&new,,s)) %then ¤t;
%else ¤t &new;
%mend;
Which you could then use like this:
%let parameter_macrovariable = %check_and_add(¶meter_macrovariable.,¶meter_value.);
This Code gives this ERROR: Invalid Macro-Variable is already used by chkvaluelist_Add. ERROR 180-322: Statement is not valid or it is used out of proper order:
%global parameter_macrovariable;
%let parameter_macrovariable =;
%MACRO ChkValueList_Add(p_list =, p_value =);
%if %sysfunc(findw(&p_list., %p_value.,,s)) %then %do;
&p_list.;
%end;
%else %do;
&p_list. &p_value.;
%end;
%MEND;
ChkValueList_Add(p_list = ¶meter_macrovariable . ,p_value = Value1);
Because it looks nothing like the code I shared.
Why did you have the macro generate extra semicolons? That would make impossible to use anywhere other than the end of a statement.
Why did you call the macro outside of a context where the generated text would make sense?
That would be like running:
%let x=Hello world;
&x;
Instead of
%let x=Hello World;
%put &x ;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.