1
votes

I'm trying to build a macro around the solution to this question

My reproducible example here doesn't do anything useful, it's just to highlight the syntax error I'm getting.

The line rc = mx.defineKey(&groups) works in the first case, executing rc = mx.defineKey('grp1','grp2').

In the second case however, where I define &groups differently but aiming at the same value, it fails with error:

NOTE: Line generated by the macro variable "GROUPS". 21
'grp1','grp2' _ 386 _ 200 76 MPRINT(TEST2): rc = mx.defineKey('grp1','grp2'); ERROR: DATA STEP Component Object failure. Aborted during the COMPILATION phase. ERROR 386-185: Expecting an arithmetic expression.

ERROR 200-322: The symbol is not recognized and will be ignored.

ERROR 76-322: Syntax error, statement will be ignored.

Here are the working example followed by the non working one. I would like to:

  • Understand why second case is not working
  • Get it to work by defining groups from grp_list the appropriate way.

Reproducible code:

data have;
input grp1 $ grp2 $ number;
datalines;
a c 3
b d 4
;

%macro test;
data want;
set have;
if _n_ = 1 then do;
    declare hash mx();
    %let groups = 'grp1','grp2';
    %put rc = mx.defineKey(&groups);
    %put rc = mx.defineKey('grp1','grp2');
    rc = mx.defineKey(&groups);
    rc = mx.definedata('number');
    rc = mx.definedone();
end;
run;
%mend;

%test

%macro test2;
data want;
set have;
if _n_ = 1 then do;
    declare hash mx();
    %let grp_list = grp1 grp2;
    %let groups = %str(%')%qsysfunc(tranwrd(&grp_list,%str( ),%str(%',%')))%str(%');
    %put rc = mx.defineKey(&groups);
    %put rc = mx.defineKey('grp1','grp2');
    rc = mx.defineKey(&groups);
    rc = mx.definedata('number');
    rc = mx.definedone();
end;
run;
%mend;

%test2
3

3 Answers

2
votes

You need to remove the macro quoting as it is adding invisible control characters that are messing up your hash definition. Change the relevant line to this:

rc = mx.defineKey(%unquote(&groups));

You might also consider defining a separate macro to quote each of your list items, and using double quotes rather than single quotes, e.g.

%macro quotelist(list);
  %local i word;
  %do %while(1);
    %let i = %eval(&i + 1);
    %let word = %scan(&list,&i,%str( ));
    %if &word = %then %return;
%sysfunc(quote(&word)) /*De-indent to avoid adding extra spaces*/
  %end;
%mend;

%put %sysfunc(translate(%quotelist(a b c),%str(,),%str( )));

This avoids the additional hassles associated with resolving macro variables inside single quotes.

1
votes

Don't embed your macro logic into the middle of the DATA step. It might confuse you into thinking that it runs while the data step is running instead of running before the data step as it actually does.

Why are you jumping through hoops to add single quotes? Just use double quotes.

%macro test2;
%let grp_list = grp1 grp2;
%let groups = "%sysfunc(tranwrd(&grp_list,%str( ),"%str(,)"))";
%put rc = mx.defineKey(&groups);
%put rc = mx.defineKey("grp1","grp2");
data want;
  set have;
  if _n_ = 1 then do;
    declare hash mx();
    rc = mx.defineKey(&groups);
    rc = mx.definedata("number");
    rc = mx.definedone();
  end;
run;
%mend test2;
0
votes

This problem is due to macro quoting. It is fine when you print it to log via put. However, when you call it with defineKey methods. SAS parser sees additional quotations. That is way you get it failed at complication phase, before execution. (The symbol is not recognized - that is the macro quoting). If you remove the macro quoting, it will work.

%macro test2;
data want;
set have;
if _n_ = 1 then do;
    declare hash mx();
    %let grp_list = grp1 grp2;
    %let groups = %str(%')%qsysfunc(tranwrd(&grp_list,%str( ),%str(%',%')))%str(%');
    %put rc = mx.defineKey(&groups);
    %put rc = mx.defineKey('grp1','grp2');
    rc = mx.defineKey(%unquote(&groups));
    rc = mx.definedata('number');
    rc = mx.definedone();
end;
run;
%mend;