BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
BenConner
Pyrite | Level 9

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

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

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

 

View solution in original post

8 REPLIES 8
Reeza
Super User

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(&quotelst) EQ 0 %then %let quotelst=&quote.%qscan(&str,&i,%str( ))&quote;
    %else %let quotelst=&quotelst.&quote.%qscan(&str,&i,%str( ))&quote;
    %let i=%eval(&i + 1);
    %if %length(%qscan(&str,&i,%str( ))) GT 0 %then %let quotelst=&quotelst.&delim;
  %end;
%unquote(&quotelst)
%mend quotelst;
BenConner
Pyrite | Level 9

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

ballardw
Super User

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;
Astounding
PROC Star

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

Tom
Super User Tom
Super User

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

 

Quentin
Super User

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.
BASUG is hosting free webinars Next up: Jane Eslinger presenting PROC REPORT and the ODS EXCEL destination on Mar 27 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
BenConner
Pyrite | Level 9

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

Ron_MacroMaven
Lapis Lazuli | Level 10

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

 

http://www.sascommunity.org/wiki/Macro_CallMacr

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

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
  • 8 replies
  • 1336 views
  • 1 like
  • 7 in conversation