BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
Mazi
Pyrite | Level 9
%global macrovariable;

%let macrovariable = Value1;

%macro check_and_add(parameter_macrovariable =, parameter_value =);
    %if ^%index(%bquote(&&&parameter_macrovariable), &parameter_value) %then %do;
        %let &parameter_macrovariable = &&&parameter_macrovariable. &parameter_value.;
		%put Updated list: &parameter_macrovariable., new added value: &parameter_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 🙂
@

obvisou
Fluorite | Level 6

Unfortunately it does not work.

ERROR: Expecting a variable name after %let.

yabwon
Onyx | Level 15

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

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



Tom
Super User Tom
Super User

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
Tom
Super User Tom
Super User

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.

obvisou
Fluorite | Level 6

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 🙂

Tom
Super User Tom
Super User

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.

obvisou
Fluorite | Level 6

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);

 

Tom
Super User Tom
Super User

@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 ...
obvisou
Fluorite | Level 6

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 = &parameter_macrovariable. &parameter_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.

Mazi
Pyrite | Level 9
@obvisou,

Can you look at what I posted earlier and let me know if it works for you or not please?
Tom
Super User Tom
Super User

@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 = &parameter_macrovariable. &parameter_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 &parameter_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))

 

Tom
Super User Tom
Super User

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(&current,&new,,s)) %then &current;
%else &current &new;
%mend;

Which you could then use like this:

%let parameter_macrovariable = %check_and_add(&parameter_macrovariable.,&parameter_value.);
obvisou
Fluorite | Level 6

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 = &parameter_macrovariable .  ,p_value = Value1);
Tom
Super User Tom
Super User

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 ;

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!
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
  • 32 replies
  • 1137 views
  • 1 like
  • 5 in conversation