Search code examples
databasefunctionsasglobal-variablessas-macro

SAS: Pass a variable's value into a Macro Function


I'm relatively new to macro functions. I'm trying to build a function that, when supplied with numeric parameter "N", will return the Nth word from a macro variable. I've been mostly successful, but for some reason the function I've created works when supplying a number, but not when supplying a numeric variable as argument.

Here's a paired down Macro list:

/*Please note these are generalized versions*/
%let MACRO_LIST = "A" "B" "C" "D" "E" "F" "G";

This is the exact function I'm using. It works when a number is supplied as argument, but does not work when that number is itself stored as a variable.

/*Return item at supplied position N from macro_list*/;
%macro Nth_ITEM(n);
    %SCAN(%superq(MACRO_LIST), &n);
%mend Nth_ITEM;

Here's a simplified example of where the error occurs.

/*CODE PRODUCING THE ERROR:*/
data my_data;

    do i = 1 to 10;
        ID_no = i;
        Identifier = %Nth_ITEM(i); /*raises*/
        output;
    end;

    drop i;
run;

/* THE FOLLOWING ERRORS ARE THROWN:

ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 
       i 
ERROR: Argument 2 to macro function %SCAN is not a number.

ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, 
              a missing value, INPUT, PUT.  

ERROR: The macro NTH_ITEM will stop executing.
*/

But directly supplying a number works

/*THIS WORKS??:*/
data my_data;

    do i = 1 to 10;
        ID_no = i;
        Identifier = %Nth_ITEM(4); /*Sets Identifier = "D" for all 10 rows*/
        output;
    end;

    drop i;
run;

Ideally, the resulting (extremely simplified) data would look like this:

ID_no Identifier
1 A
2 B
.... ....
10 G

What am I doing wrong with the macro function call? Why does it work when a number is supplied, but not when a variable is supplied? Is there something I can change to make work the way I'd like it to, or is something fundamentally wrong with this approach?

Thank all

EDIT: Just realized I only included 7 letters, pretend there are 10 letters in MACRO_LIST


Solution

  • This is because the macro is run before the data step code is compiled. Think of macros as a very fancy form of copy/paste. SAS is going to compile or run any macros, then compile the data step code. i does not yet exist as far as the macro is concerned. Constants will work since they are already initialized: it's a valid value that the macro can use.

    You do not need to use a macro to do this. You can modify your macro list to be space-separated, then use scan(). It's generally a good practice to not include quotes in your macro list when possible. You can resolve macros within double-quotes.

    %let MACRO_LIST = A B C D E F G;
    
    data my_data;
    
        do i = 1 to 10;
            ID_no = i;
            Identifier = scan("&macro_list", i);
            output;
        end;
    
        drop i;
    run;
    

    Before the data step code is compiled, &macro_list will resolve. SAS will see this in the data step:

    Identifier = scan("A B C D E F G", i);