Question

Say I have a macro varaiable x that can contain any number of alpha-only words each separated by one or more spaces:

%let x =   A B      CD   ;

I want to create another macro variable that will give this result when printed (single quotes could also be exchanged for double quotes):

'A','B','CD'

It's for use in an in() statement... ie. I'd to write code that looks something like this:

data want;
  set have;
  where my_field in (   /* DO SOMETHING TO &X HERE TO GIVE DESIRED OUTPUT */  ) ;
run;

I'm looking for a (preferably one-line) pure-macro code solution. One that doesn't generate this note:

NOTE 49-169: The meaning of an identifier after a quoted string might change in a
future SAS release.  Inserting white space between a quoted string and the succeeding
identifier is recommended.

So far I have this:

%let y = %str(%')%sysfunc(tranwrd(  %sysfunc(compbl(&x)) ,%str( ),%str(%',%') ))%str(%');
%put &y;

But I can't get rid of that darn note.

Was it helpful?

Solution

For the FCMP try with CATS(), you need to add an LENGTH statement for the result variable. FCMP handles character variables slightly differently from the Data Step.

proc fcmp outlib=work.funcs.funcs;

  function delimit_words(iString $) $; 
    length result $200; 
    result = cats("'",tranwrd(cats(compbl(iString))," ", "','"),"'");    
    return (result );
  endsub;

run;

options cmplib=work.funcs;

data _null_;
  x = "  A  B  C   ";
  y = delimit_words(x);
  put y=;
run;

Returns:

5136   data _null_;
5137     x = "  A  B  C   ";
5138     y = delimit_words(x);
5139     put y=;
5140   run;

y='A','B','C'

OTHER TIPS

If you change your if condition to use indexw instead of in then I think you can avoid this problem entirely:

data want;
  set have;
  if indexw("&X",my_field) then do; 
    output;
  end;
run;

Got it. The trick was when adding quotes, to make sure they weren't adjacent to the word boundaries by inserting a space. So initially the string is padded with additional spaces. Then right at the end, I remove the additional spaces with compress(). That way we can sneak past the test for quoted string identifiers!

%let x =   A  B  C   ;
%let y = %sysfunc(compress(%str(%' )%sysfunc(tranwrd(%sysfunc(compbl(&x)), %str( ), %str( %',%' ) ))%str( %') ));
%put &y;

Result:

'A','B','C'

Kinda ugly though.

EDIT: 2nd Attempt:

Thought I'd cheat and use proc fcmp. End result is much nicer:

proc fcmp outlib=common.funcs.funcs;

  function delimit_words(iString $) $;    
    result = "'" || tranwrd(cats(compbl(iString))," ", "','") || "'";    
    return (result );
  endsub;

run;

Datastep example:

data x;
  x = "  A  B  C   ";
  y = delimit_words(x);
  put y=;
run;

Improved macro example:

%let x =   A  B  C   ;
%let y = %sysfunc(delimit_words(&x));
%put &y;

For some reason proc fcmp was giving me unexpected results if I tried to use the cats() function instead of the || concatenators so I just went with ||. The function could be improved by allowing the user to specify what symbol(s) constitute a word boundary, as well as whether or not to quote the output, and if so, whether to use single or double quotes... but I'll add those in on an as-needed basis.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top