DATA Step, Macro, Functions and more

generating lists out of a macro invocation

Accepted Solution Solved
Reply
Regular Contributor
Posts: 179
Accepted Solution

generating lists out of a macro invocation

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


Accepted Solutions
Solution
‎07-27-2017 10:34 AM
Super User
Super User
Posts: 7,039

Re: generating lists out of a macro invocation

[ Edited ]
Posted in reply to BenConner

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


All Replies
Super User
Posts: 19,777

Re: generating lists out of a macro invocation

Posted in reply to BenConner

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;
Regular Contributor
Posts: 179

Re: generating lists out of a macro invocation

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

Super User
Posts: 11,343

Re: generating lists out of a macro invocation

Posted in reply to BenConner

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;
Super User
Posts: 5,499

Re: generating lists out of a macro invocation

[ Edited ]
Posted in reply to BenConner

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

Solution
‎07-27-2017 10:34 AM
Super User
Super User
Posts: 7,039

Re: generating lists out of a macro invocation

[ Edited ]
Posted in reply to BenConner

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

 

PROC Star
Posts: 1,322

Re: generating lists out of a macro invocation

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.
Regular Contributor
Posts: 179

Re: generating lists out of a macro invocation

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

Regular Contributor
Posts: 227

Re: generating lists out of a macro invocation

Posted in reply to BenConner

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

☑ This topic is solved.

Need further help from the community? Please ask a new question.

Discussion stats
  • 8 replies
  • 222 views
  • 1 like
  • 7 in conversation