Hi,
I'm chasing my tail on what should be a simple macro and getting nowhere. Given the following code:
%macro stlist(vars,q=%str( ), c=%str( ));
%let i=1;
%do %while (%scan(&vars,&i) ^= );
%let sv = %scan(&vars,&i);
%if &i ^= 1 %then &c;
%let res=&q.&sv.&q;
&res
%let i= %eval(&i+1);
%end;
%mend stlist;
%let tvars=cat dog pig;
data _null_;
* Goal: produce the following statement...
array t(3) $ a b c ('cat' 'dog' 'pig');
array t(3) $ a b c ( %stlist(&tvars, q=%str(%')) );
r="%stlist(&tvars, q=%str(%'))";
put r=;
stop;
run;
If I run this as-is, SAS throws a syntax error. If I comment out the array statement and let the result of the macro invocation be assigned to the variable r, it give me what I want for the intial values for a b and c in the array statement.
What am I missing?
Thanks!
--Ben
Your macro is way over complicated and is missing %LOCAL statement for I SV and RES.
But the real problem is that you are generating macro quoted strings that are then causing SAS to not understand what code you wanted. You could probably remove the errors by just using %UNQUOTE() macro function to remove the macro quoting that you added.
array t(3) $ a b c ( %unquote(%stlist(&tvars, q=%str(%'))) );
Here is simple code to take a space delimited list of words (without any embedded quote characters) and return a quoted list.
"%sysfunc(tranwrd(&tvars,%str( )," "))"
Now this will not work if you list has more than one space between the words, but you can use COMPBL() to fix that. So if the list of variables is user entered then use this line to clean it up to one space between variable name.
%let tvars=%sysfunc(compbl(&tvars));
So here is a little example including using the list to generate initial values for an array ;
%let tvars=cat dog fox;
%let tvars=%sysfunc(compbl(&tvars));
%let n=%sysfunc(countw(&tvars));
data _null_;
array t (&n) $32 _temporary_ ("%sysfunc(tranwrd(&tvars,%str( )," "))");
xx = catx(' ',of t(*));
put xx=;
run;
xx=cat dog fox NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
If you want a flexible tool for generating lists in different forms use this %QLIST() macro.
https://github.com/sasutils/macros/blob/master/qlist.sas
Here are some examples
%let tlist=dog cat fox;
%put %qlist(&tlist);
%put %qlist(&tlist,paren=0);
%put %qlist(&tlist,quote=2);
%put %qlist(&tlist,comma=0);
%put %qlist(A B,dsd=1);
124 %let tlist=dog cat fox; 125 %put %qlist(&tlist); ('dog','cat','fox') 126 %put %qlist(&tlist,paren=0); 'dog','cat','fox' 127 %put %qlist(&tlist,quote=2); ("dog","cat","fox") 128 %put %qlist(&tlist,comma=0); ('dog' 'cat' 'fox') 129 %put %qlist(A B,dsd=1); ('A','','B')
Where do you originally get the information from?
If it's a table when you select it you can use the QUOTE function. Otherwise, I'd probably look at the macro here to turn my list into a quoted list:
http://www.datasavantconsulting.com/roland/Spectre/utilmacros/quotelst.sas
%macro quotelst(str,quote=%str(%"),delim=%str( ));
%local i quotelst;
%let i=1;
%do %while(%length(%qscan(&str,&i,%str( ))) GT 0);
%if %length("elst) EQ 0 %then %let quotelst="e.%qscan(&str,&i,%str( ))"e;
%else %let quotelst="elst."e.%qscan(&str,&i,%str( ))"e;
%let i=%eval(&i + 1);
%if %length(%qscan(&str,&i,%str( ))) GT 0 %then %let quotelst="elst.&delim;
%end;
%unquote("elst)
%mend quotelst;
The list will be handed to the macro invocation either hard-coded or as a macro field. The input list actually is a list of SAS variable names. Some SAS statements need the names quoted, others comma separated, and still others with both commas and quotes.
I just found that using %cmpres(&res) rather than &res by itself cleared it up. Will check out the other approaches for a more elegant solution.
Thanks!
--Ben
A less generic macro relying on 1) space delimited list of words and 2) only expected to provide quotes:
I have a %put just to display the resolved version because during development I seldom have a place to use the resolved value.
%macro stlist(vars); %let words = %sysfunc(countw(&vars)); %let res=; %if &words > 0 %then %do i = 1 %to &words; %let res= &res %sysfunc(quote(%scan(&vars,&i))); %end; %put &res; %mend stlist;
First note, this particular application doesn't require single quotes. Double quotes would work just fine. For that:
%macro stlist (list=);
%local i;
%do i=1 %to %sysfunc(countw(&list));
"%scan(&list, &i)"
%end;
%mend stlist;
If you really need single quotes, you can replace the interior statement with:
%unquote(%str(%'%scan(&list,&i)%'))
Calling the macro:
%stlist (cat dog pig)
You can use this anywhere you want the quoted text to appear. That could be inside the ARRAY statement, but it could also be here:
%let r = %stlist(cat dog pig);
Your macro is way over complicated and is missing %LOCAL statement for I SV and RES.
But the real problem is that you are generating macro quoted strings that are then causing SAS to not understand what code you wanted. You could probably remove the errors by just using %UNQUOTE() macro function to remove the macro quoting that you added.
array t(3) $ a b c ( %unquote(%stlist(&tvars, q=%str(%'))) );
Here is simple code to take a space delimited list of words (without any embedded quote characters) and return a quoted list.
"%sysfunc(tranwrd(&tvars,%str( )," "))"
Now this will not work if you list has more than one space between the words, but you can use COMPBL() to fix that. So if the list of variables is user entered then use this line to clean it up to one space between variable name.
%let tvars=%sysfunc(compbl(&tvars));
So here is a little example including using the list to generate initial values for an array ;
%let tvars=cat dog fox;
%let tvars=%sysfunc(compbl(&tvars));
%let n=%sysfunc(countw(&tvars));
data _null_;
array t (&n) $32 _temporary_ ("%sysfunc(tranwrd(&tvars,%str( )," "))");
xx = catx(' ',of t(*));
put xx=;
run;
xx=cat dog fox NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
If you want a flexible tool for generating lists in different forms use this %QLIST() macro.
https://github.com/sasutils/macros/blob/master/qlist.sas
Here are some examples
%let tlist=dog cat fox;
%put %qlist(&tlist);
%put %qlist(&tlist,paren=0);
%put %qlist(&tlist,quote=2);
%put %qlist(&tlist,comma=0);
%put %qlist(A B,dsd=1);
124 %let tlist=dog cat fox; 125 %put %qlist(&tlist); ('dog','cat','fox') 126 %put %qlist(&tlist,paren=0); 'dog','cat','fox' 127 %put %qlist(&tlist,quote=2); ("dog","cat","fox") 128 %put %qlist(&tlist,comma=0); ('dog' 'cat' 'fox') 129 %put %qlist(A B,dsd=1); ('A','','B')
Just plugging my favorite list manipulation macro, Richard DeVenezia's %seplist:
http://www.devenezia.com/downloads/sas/macros/index.php?m=seplist
I added an %UNQUOTE to the last line of the macro definition, i.e.:
%unquote(&emit)
It allows you to add/remove/modify delimiters as well as prefixes/suffixes and also nest items
Useful for simple list manipulation:
128 %let tlist= dog cat fox; 129 130 %put %seplist(&tlist); dog,cat,fox 131 132 %put %seplist(&tlist,nest=Q); 'dog','cat','fox' 133 134 %put %seplist(&tlist,nest=QQ); "dog","cat","fox" 135 136 %put %seplist(&tlist,nest=QQ,dlm=%str( )); "dog" "cat" "fox"
And can even generate blocks of code:
139 %seplist(shoes class prdsale 140 ,prefix=proc print data=sashelp. 141 ,suffix=%str((obs=5);run;) 142 ,dlm=%str( ) 143 ) MPRINT(SEPLIST): proc print data=sashelp.shoes(obs=5); MPRINT(SEPLIST): run; NOTE: There were 5 observations read from the data set SASHELP.SHOES. MPRINT(SEPLIST): proc print data=sashelp.class(obs=5); MPRINT(SEPLIST): run; NOTE: There were 5 observations read from the data set SASHELP.CLASS. MPRINT(SEPLIST): proc print data=sashelp.prdsale(obs=5); MPRINT(SEPLIST): run; NOTE: There were 5 observations read from the data set SASHELP.PRDSALE.
Wow. Obviously this has been considered before. 😉 I don't even know where to begin. Thanks to all for pointing out what I had overlooked, and especially for sharing better code! Careful thought beats quick and dirty every time...
Greatly appreciated!
--Ben
I have written a macro named calltext that can handle this problem;
your problem required a hard-coded (typed in) list.
macro calltext reads a list, a control data set and provides the desired string from the values in the data set
http://www.sascommunity.org/wiki/Macro_CallText
Ron Fehd list processing maven
see also macro Call-Macro, which returns a macro call from the values in the data set
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.