I have inherited macro in which the below block does not seem to be resolved. The macro is supposed to conditionally select the logical operations based on the date values. If the %if conditions are satisfied, they should print the macro variables mentioned in the %put statement, which in-turn become part of the if condition. If the condition %if %length(&datein.)=10 OR %length(ST_&date.)=10 is satisfied, it would print the first %put statement else the second %put statement would be printed. Same logic applies to the second %if statement.
The macro is supposed to be run inside a data step. The logical operator AND is being considered as a variable and I'm getting a note NOTE: Variable AND is uninitialized.
I tried to balance the if condition by adding the open and closed parenthesis mentioned in below code. Tried to change the structure of the if condition to separate the first and the second condition, but doesn't seem to work. I assuming that the logical operator AND cannot be used as it is in the %if %then %else statement.
%macro test_date(date=, comp1=, comp2=, label=);
if &datein. ne "" and ST_&date. ne "" then do;
if (%if %length(&datein.)=10 OR %length(ST_&date.)=10 %then %put ST_&date._10 &comp1. datein_10; %else %put ST_&date._19 &comp1. datein_19;
AND %if %length(&datein.)=10 OR %length(ED_&date.)=10 %then %put datein_10 &comp2. ED_&date._10; %else %put datein_19 &comp2. ED_&date._19;)
then EPOCH=&label.;
end;
%mend test_date;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
You will probably need to adapt this a bit, but it should be helpful in clearifying the logic of it. The code below checks the length of macro variables and inserts data step code based on this length. It will also print some values to the log, but that doesn't affect results. I've included some simple data step examples to illustrate how it works.
%macro test_date(date=, comp1=, comp2=, label=);
%if &datein. ne and &&ST_&date. ne %then %do;
%if %length(&datein.)=10 OR %length(&&ST_&date.)=10 %then %do;
/* Insert data step code */
if variable1=1 then epoch="First condition";
else epoch="firstelse";
/* Print to log*/
%put &&ST_&date. &comp1. &datein.;
%end;
%else %if %length(&datein.)=10 OR %length(&&ED_&date.)=10 %then %do;
if variable1=0 then epoch="Second condition";
else epoch="secondelse";
%end;
%end;
%mend test_date;
/* Macro variables given values of length 10. Outer if and first condition in do block holds.*/
%let datein=0123456789;
%let st_screen=0123456789;
%let ed_screen=0123456789;
data test1;
variable1=1;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
run;
data test2;
variable1=0;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
run;
/* Macro variables given values of length 9 and 10. Outer if and else condition in do block holds.*/
%let datein=012345678;
%let st_screen=012345678;
%let ed_screen=0123456789;
data test3;
variable1=1;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
run;
data test4;
variable1=0;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
run;
/* Macro variables are empty. Outer if is false. The macro does nothing. */
%let datein=;
%let st_screen=;
%let ed_screen=;
data test5;
variable1=1;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
run;
data test6;
variable1=0;
%test_date(date=SCREEN, comp1= le, comp2= le, label='SCREEN');
run;
Your program does not make any sense. If we ignore the %IF statements that are just potentially generating messages to the LOG your macro is generating this SAS code:
if &datein. ne "" and ST_&date. ne "" then do;
if ( AND ) then EPOCH=&label.;
end;
Since you are using the variable AND as the condition for the IF statement SAS will assume that it is a numeric variable and test whether its value is non-zero and non-missing. If there is no variable named AND then its value will be missing so the value of EPOCH will never be changed.
If you want the macro to do those tests of macro variable lengths then pull that macro logic out of the middle of the SAS code you are trying to generate.
Let's look at the first %IF/%THEN statement.
%if %length(&datein.)=10 OR %length(ST_&date.)=10
%then %put ST_&date._10 &comp1. datein_10;
%else %put ST_&date._19 &comp1. datein_19;
So you are testing if the length of the macro variable DATEIN is 10 bytes or the length of the macro variable DATE is 7 bytes (the constant text ST_ will always be 3 bytes long).
Related
Suppose I like to do something like the following (with exemplary variable names for better readability):
take a parameter InParameter and match it to the variable MyVar1 in a dataset MyData
return all values for the variable MyVar2 for the filtered observations
from a subroutine/function
that i can use inside proc sql/datastep
This is what I got so far (clearly not working):
proc fcmp outlib=work.funcs.MyFunction;
function MyFunction(InParameter $);
array MyArray ... ; /* Here: Create an array with something like SELECT MyVar2 FROM MyData WHERE MyVar1 = Inparameter */
return(MyArray{});
endsub;
;
quit;
options cmplib=work.funcs;
data MyOutput;
set Somedata;
if MyVar2 in MyFunction("H20") then output;
run;
In short:
can data in datasets be accessed from inside a function/subroutine?
can a function/subroutine return an array?
Thanks for your help!
We created a utility macro called %ds2list() that will perform your desired process. It doesn't use an array statement but it achieves the same result.
The macro simply returns values from a dataset in a list format. Here's an example of calling it:
%put %ds2list(iDs=sashelp.class, iField=name, iQuote=1);
This would return:
'Alfred','Alice','Barbara','Carol','Henry','James','Jane','Janet','Jeffrey','John','Joyce','Judy','Louise','Mary','Philip','Robert','Ronald','Thomas','William'
The default behavior for %ds2list() is to comma separate the returned values but it is very flexible. You can change the delimiter to a value of your choice (or no delimiter), you can turn the quotes on or off, or change them from single to double quotes, and you can provide any dataset options you would normally use on a set statement such as a where=() statement.
Additionally because the macro is pure macro code you can use this literally anywhere in SAS. In any proc/data/macro you like. We use it extensively for calls to ODBC passthrough when we have a large list of IDs we want to be returned.
Here's an example of how you could use it. First create a table that will contain values to compare against the list values:
data keep;
input name $;
datalines;
Alfred
Carol
Janet
run;
Iterate over the values we want to check against the list:
data want;
set keep;
if name in (%ds2list(iDs=sashelp.class, iField=name, iQuote=1, iDsOptions=where=(sex='F'))) then do;
output;
end;
run;
Returns:
Obs name
=== =====
1 Carol
2 Janet
You can see Alfred was excluded from the result because he was filtered out by the where=() clause.
Here is the macro, I suggest putting it in your macro autocall library:
/***************************************************************************
** PROGRAM: MACRO.DS2LIST.SAS
**
** UTILITY PROGRAM THAT DETECTS RETURNS A LIST OF FIELD VALUES FROM A
** DATASET IN DELIMITED FORMAT.
**
** PARAMETERS:
** iDs : THE LIBNAME.DATASET NAME THAT YOU WANT TO CHECK.
** iField : THE FIELD THAT CONTAINS THE VALUES YOU WANT RETURNED IN A
** DELIMITED FORMAT.
** iDelimiter: DEFAULT IS A COMMA. THE DELIMITER TO USE FOR THE RETURNED LIST.
** iDsOptions: ANY STANDARD DATASET OPTIONS THAT YOU WOULD LIKE TO APPLY SUCH
** AS A WHERE STATEMENT.
** iQuote : (0=NO,1=YES). DEFAULT=0/NO. DETERMINES WHETHER THE RETURNED
** LIST IS QUOTED OR NOT.
** iQuoteChar: (SINGLE,DOUBLE) DEFAULT=SINGLE. SPECIFIES WHETHER SINGLE
** OR DOUBLE QUOTES ARE USED WHEN QUOTING THE RETURNED LIST
**
*****************************************************************************/
%macro ds2list(iDs=, iField=, iDsOptions=, iDelimiter=%str(,), iQuote=0, iQuoteChar=single);
%local dsid pos rc result cnt quotechar value;
%let result=;
%let cnt=0;
%if &iQuote %then %do;
%if "%upcase(&iQuoteChar)" eq "DOUBLE" %then %do;
%let quotechar = %nrstr(%");
%end;
%else %if "%upcase(&iQuoteChar)" eq "SINGLE" %then %do;
%let quotechar = %nrstr(%');
%end;
%else %do;
%let quotechar = %nrstr(%");
%put WARNING: MACRO.DS2LIST.SAS: PARAMETER IQUOTECHAR INCORRECT. DEFAULTED TO DOUBLE;
%end;
%end;
%else %do;
%let quotechar = ;
%end;
/*
** ENSURE ALL THE REQUIRED PARAMETERS WERE PASSED IN.
*/
%if "&iDs" ne "" and "&iField" ne "" %then %do;
%let dsid=%sysfunc(open(&iDs(&iDsOptions),i));
%if &dsid %then %do;
%let pos=%sysfunc(varnum(&dsid,&iField));
%if &pos %then %do;
%let rc=%sysfunc(fetch(&dsid));
%do %while (&rc eq 0);
%if "%sysfunc(vartype(&dsid,&pos))" = "C" %then %do;
%let value = %qsysfunc(getvarc(&dsid,&pos));
%if "%trim(&value)" ne "" %then %do;
%let value = %qtrim(&value);
%end;
%end;
%else %do;
%let value = %sysfunc(getvarn(&dsid,&pos));
%end;
/* WHITESPACE/CARRIAGE RETURNS REMOVED IN THE BELOW LINE */
/* TO ENSURE NO WHITESPACE IS RETURNED IN THE OUTPUT. */
%if &cnt ne 0 %then %do;%unquote(&iDelimiter)%end;%unquote("echar&value"echar.)
%let cnt = %eval(&cnt + 1);
%let rc = %sysfunc(fetch(&dsid));
%end;
%if &rc ne -1 %then %do;
%put WARNING: MACRO.DS2LIST.SAS: %sysfunc(sysmsg());
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: FIELD &iField NOT FOUND IN DATASET %upcase(&iDs).;
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: DATASET %upcase(&iDs) COULD NOT BE OPENED.;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: YOU MUST SPECIFY BOTH THE IDS AND IFIELD PARAMETERS TO CALL THIS MACRO.;
%end;
%mend;
Not sure that a function would work with the IN operator. You might need to wrap the function call with a macro to generate the proper syntax. In which case why not just make a macro to begin with?
Here is generic macro to extract the values from a variable in a dataset.
%macro varlist
/*----------------------------------------------------------------------
Generate list of values from dataset
----------------------------------------------------------------------*/
(dataset /* Input dataset */
,variable /* Variable Name */
,quote=1 /* Add quotes around values? 1=Single 2=Double */
,comma=1 /* Add comma between values? */
,paren=1 /* Add parentheses around results? */
);
%local did sep &variable ;
%if &paren=1 %then (;
%let did=%sysfunc(open(&dataset));
%syscall set(did);
%do %while(0=%sysfunc(fetch(&did)));
%let &variable=%qsysfunc(trim(%superq(&variable)));
%if "e=1 %then &sep.%sysfunc(quote(&&&variable,%str(%')));
%else %if "e=2 %then &sep.%sysfunc(quote(&&&variable));
%else &sep.&&&variable;
%if &comma=1 %then %let sep=,;
%end;
%let did=%sysfunc(close(&did));
%if &paren=1 %then );
%mend varlist;
Example calls:
%put %varlist(sashelp.class,name);
%put %varlist(sashelp.class(where=(sex='M')),age,quote=0,comma=0);
So in your case you might use it like this:
data MyOutput;
set Somedata;
where MyVar2 in %varlist(Mydata(where=(MyVar1="H20")),MyVar2) ;
run;
You are better off with a macro.
%macro subset(inParameter, indata, outdata);
proc sql noprint;
create table &outdata as
select * from &indata
where myVar2 in (select distinct myVar2 from myData where myVar1 = "&inParameter);
quit;
%mend;
%subst(H20,Somedata,MyOutput);
Below is the code to execute a set of data-steps based on the value of the increment variable "i". Since I have assigned the value of i to 1 (numeric and not character value). Ideally the first data-step block need to execute, but in the below case the second data-step block is executing.
%put &i. ; prints 1 in to the log window.
%macro DSN;
%let i = 1 ;
data new_DSN;
run;
%if i = 1 %then %do;
data Dummy ;
run;
data DUMMY_ ;
set DUMMY new_DSN ;
run;
%end;
%else %if i ^= 1 %then %do ;
data DUMMY_ ;
set DUMMY_ new_DSN ;
run;
%end;
%mend DSN;
%DSN;
Your IF statement is not calling &I macro variable, but simply comparing string I to 1. This also explains why your second loop running because technically speaking string "I" is not equal to "1". You just need to put ampersand in front of I in both %IF statements. I also put two %PUT statements to easier see where code is running. See below:
%macro DSN;
%let i = 1 ;
data new_DSN;
run;
%if &i = 1 %then %do;
%PUT First Loop Run;
data Dummy ;
run;
data DUMMY_ ;
set DUMMY new_DSN ;
run;
%end;
%else %if &i ^= 1 %then %do ;
%PUT Second Loop Run;
data DUMMY_ ;
set DUMMY_ new_DSN ;
run;
%end;
%mend DSN;
%DSN;
I'm getting an error while running the below code. &CNT is 50 and &vars has column names in it.
Each column as some values from 1 to 100. I want to select each column and check the below criteria (%if statement), creating a new variable and assigning the values to it (like free, partially free and not free).
option mlogic mprint;
%macro analysis();
DATA Q2;
SET Q1;
%do i=1 %to &CNT.;
%let segs =%scan(&VARS.,&i.," ");
%IF &SEGS.<=2.5 %THEN &SEGS._R="FREE";
%ELSE %IF (&SEGS.>2.5 AND &SEGS.<5.5) %THEN &SEGS._R="PARTLY FREE";
%ELSE %IF &SEGS.>=5.5 %THEN &SEGS._R="NOT FREE";
/*%PUT &segs.;*/
%end;
RUN;
%MEND;
%analysis();
This is the output I'm getting:
SAS LOG ERROR:
MPRINT(ANALYSIS): DATA Q2;
MPRINT(ANALYSIS): SET Q1;
MLOGIC(ANALYSIS): %DO loop beginning; index variable I; start value is 1; stop value is 56; by value
is 1.
MLOGIC(ANALYSIS): %LET (variable name is SEGS)
MLOGIC(ANALYSIS): %IF condition &SEGS.<=2.5 is FALSE
MLOGIC(ANALYSIS): %IF condition (&SEGS.>2.5 AND &SEGS.<5.5) is FALSE
MLOGIC(ANALYSIS): %IF condition &SEGS.>=5.5 is TRUE
MLOGIC(ANALYSIS): %PUT &segs.
yr1960
MLOGIC(ANALYSIS): %DO loop index variable I is now 2; loop will iterate again.
MLOGIC(ANALYSIS): %LET (variable name is SEGS)
MLOGIC(ANALYSIS): %IF condition &SEGS.<=2.5 is FALSE
MLOGIC(ANALYSIS): %IF condition (&SEGS.>2.5 AND &SEGS.<5.5) is FALSE
MLOGIC(ANALYSIS): %IF condition &SEGS.>=5.5 is TRUE
***NOTE: Line generated by the macro variable "SEGS".
1 yr1961_R
--------
22
You are confusing IF conditions inside the Macro processor versus inside the Data Step. I think this is what you want.
%macro analysis();
DATA Q2;
SET Q1;
%do i=1 %to &CNT.;
%let segs =%scan(&VARS.,&i.," ");
IF &SEGS.<=2.5 THEN &SEGS._R="FREE";
ELSE IF (&SEGS.>2.5 AND &SEGS.<5.5) THEN &SEGS._R="PARTLY FREE";
ELSE IF &SEGS.>=5.5 THEN &SEGS._R="NOT FREE";
/*%PUT &segs.;*/
%end;
RUN;
%MEND;
%analysis();
Macros write code for you. You were comparing the variable Name to the constant values (using string ordering, no less), not the variable values versus the constant values using numbers.
I have a table with one row and 4 columns. I would like to create 4 macro variables named after each column where the value is set to the value in the 1 row.
If this were R I could access the values directly with something like:
newvar1=tablename[1,1]
newvar2=tablename[1,2]...
Is there anyway for me to select values from a table and set macrovariables equal to that value?
So something like:
%macrovar1=tablename[1,1]...
Except obviously the right side of the equals sign is R code not SAS.
Thanks
You can use proc sql to do it like so:
proc sql noprint inobs=1;
select name into :my_val from sashelp.class;
quit;
%put &my_val;
Or you can use call symput from the datastep like this:
data _null_;
set sashelp.class(obs=1);
call symput('my_val',name);
run;
For something more flexibile, we use a utility macro that allows us to check values from anywhere in our code. I've modified it slightly to accommodate your request but the usage will be like this:
%let my_val = %get_val(iDs=sashelp.class, iField=name);
You could also use it in the middle of a proc or a datastep like so:
data _null_;
my_value = "%get_val(iDs=sashelp.class, iField=name)";
run;
Or even:
proc sql noprint;
create table want as
select * from sashelp.class
where name = "%get_val(iDs=sashelp.class, iField=name)"
;
quit;
Here is the macro definition:
%macro get_val(iDs=, iField=);
%local dsid pos rc result cnt value;
%let result=;
%let cnt=0;
/*
** ENSURE ALL THE REQUIRED PARAMETERS WERE PASSED IN.
*/
%if "&iDs" ne "" and "&iField" ne "" %then %do;
%let dsid=%sysfunc(open(&iDs,i));
%if &dsid %then %do;
%let pos=%sysfunc(varnum(&dsid,&iField));
%if &pos %then %do;
%let rc=%sysfunc(fetch(&dsid));
%if "%sysfunc(vartype(&dsid,&pos))" = "C" %then %do;
%let value = %qsysfunc(getvarc(&dsid,&pos));
%if "%trim(&value)" ne "" %then %do;
%let value = %qtrim(&value);
%end;
%end;
%else %do;
%let value = %sysfunc(getvarn(&dsid,&pos));
%end;
&value
%end;
%else %do;
%put ERROR: MACRO.GET_VAL.SAS: FIELD &iField NOT FOUND IN DATASET %upcase(&iDs).;
%end;
%end;
%else %do;
%put ERROR: MACRO.GET_VAL.SAS: DATASET %upcase(&iDs) COULD NOT BE OPENED.;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put ERROR: MACRO.GET_VAL.SAS: YOU MUST SPECIFY BOTH THE IDS AND IFIELD PARAMETERS TO CALL THIS MACRO.;
%end;
%mend;
The above macro is an abbreviated version of the %ds2list macro found here.
There are several ways, but the simplest is:
data _null_;
set have;
if _n_=1 then do;
call symputx('macrovar1',var1);
*more of these;
end;
stop;
run;
That opens the dataset, then if on first row (_n_ is iteration of data step loop, which in most cases is the first row), use call symputx to assign its value to a macro variable.
I would note that you should remember that SAS macro variables are not data variables, do not have a data type (always are text), and usually are not used to store data the way you would use vectors in R.
data sample;
input x $;
datalines;
one
two
three
;
%macro variable_to_macvar(variable=, dataset=);
proc sql noprint;
select &variable into : outlist separated by ' '
from &dataset;
quit;
&outlist
%mend variable_to_macvar;
%put %variable_to_macvar(variable=x, dataset=sample);
Expected output: one two three. Instead I get an error. Why? Is this fixable?
I've successfully created other macros of a very similar form, where the function "returns" a value using the ¯ovariable at the end of the macro without a semicolon. For example, here is a similar type of function that works:
%macro zcat(first=5, last=15, prefix=freq);
%let x=;
%do i = &first %to &last;
%let x=&x &prefix.&i;
%end;
&x
%mend zcat;
%put %zcat();
You cannot execute a macro that involves running a proc or a data step in the way that you're trying to do here. You would need to use something like %sysfunc(dosubl(proc sql...)) in order for that to work (assuming you have SAS 9.3+ - see Joe's answer above). Otherwise, you can't use proc sql within a function-style macro.
More details about dosubl:
http://support.sas.com/documentation/cdl/en/lefunctionsref/67398/HTML/default/viewer.htm#p09dcftd1xxg1kn1brnjyc0q93yk.htm
It would be a bit fiddly, but if you really wanted to make this work as a function-style macro in earlier versions of SAS, you could construct it using the open, fetchobs and getvarc functions instead.
Update: Here's an example (using call set rather than getvarc, as this turned out to be simpler), in case anyone needs to do this in SAS 9.2 or earlier.
%macro variable_to_macvar(var,ds);
%local rc dsid i;
%let &var =;
%global outlist;
%let outlist=;
%let dsid = %sysfunc(open(&ds,i));
%syscall set(dsid);
%let rc = 0;
%let i = 0;
%do %while(&rc = 0);
%let i = %eval(&i + 1);
%let rc = %sysfunc(fetchobs(&dsid,&i));
%if &rc = 0 %then %let outlist = &outlist &&&var;
%end;
%let rc = %sysfunc(close(&dsid));
&outlist
%mend;
%put %variable_to_macvar(var=x, ds=sample);
Now works for views as well as ordinary datasets.
DOSUBL is available (but experimental) in 9.3 (at least, 9.3TS1M2, which I have). This is how you'd do it.
data sample;
input x $;
datalines;
one
two
three
;
%macro variable_to_macvar(variable=, dataset=);
%let rc=%sysfunc(dosubl(%str(
proc sql noprint;
select &variable into : outlist separated by ' '
from &dataset;
quit;
)));
&outlist
%mend variable_to_macvar;
%put %variable_to_macvar(variable=x, dataset=sample);;
If you can't use DOSUBL, or want to avoid experimental things, you can do this with PROC FCMP rather than a macro. If you like to write functions, PROC FCMP is probably for you: actually being able to write functions, rather than having to deal with the annoyances of the macro language.
Alter your code at the end to
%global outlist;
%variable_to_macvar(variable=x, dataset=sample);
%put &outlist;
The %put wants to resolve only a macro variable or a single value. It cannot call a procedure. So call your macro and then print the result.
Also, delete the &outlist from the macro definition. Sorry I missed that initially.
EDIT: Alternative.
Change your macro definition to
%macro variable_to_macvar(variable=, dataset=);
proc sql noprint;
select &variable into : outlist separated by ' '
from &dataset;
quit;
%put &outlist
%mend variable_to_macvar;
Just do the %put inside the macro.
%variable_to_macvar(variable=x, dataset=sample);
will print the string to the log.
We have a utility macro that is probably one of our most used pieces of code that does this for us. It is similar to the code that #user667489 provided but includes some nice features including error catching, allows both character and numeric vars, allows you to specify seperators, quotes, quote characters, filters to the dataset, etc....
We just put this macro in our autocall library so that it's avaialble to all of our programs. Some examples of running the macro:
Example 1 - Default behaviour:
%put %variable_to_macvar(var=x, ds=samplex);
Result 1:
one,two,three
Not quite the desired output as the default seperator is a comma, this is easily changed though...
Example 2 - Specify to use a space character as a delimiter:
%put %ds2list(iDs=samplex, iField=x, iDelimiter=%str( ));
Result 2:
one two three
Example 3 - Quoting & example usage
data names;
input name $;
datalines;
John
Jim
Frankie
;
run;
%put %ds2list(iDs=names, iField=name, iQuote=1);
proc sql noprint;
create table xx as
select *
from sashelp.class
where name in (%ds2list(iDs=names, iField=name, iQuote=1))
;
quit;
Result 3:
The below is printed to the log:
'John','Jim','Frankie'
Notice how we don't need to even save the result to a macro variable to use it in the SQL statement! Swweeet! This works just as well for SQL passthrough queries, and any other data step or proc statement that you can throw it at. In the above example, a single row is returned as 'John' is the only match found...
Anyway, that's our solution here... been using this for >10 years and works well for me. Here is the macro:
/***************************************************************************
** PROGRAM: MACRO.DS2LIST.SAS
**
** UTILITY PROGRAM THAT DETECTS RETURNS A LIST OF FIELD VALUES FROM A
** DATASET IN DELIMITED FORMAT.
**
** PARAMETERS:
** iDs : THE LIBNAME.DATASET NAME THAT YOU WANT TO CHECK.
** iField : THE FIELD THAT CONTAINS THE VALUES YOU WANT RETURNED IN A
** DELIMITED FORMAT.
** iDelimiter: DEFAULT IS A COMMA. THE DELIMITER TO USE FOR THE RETURNED LIST.
** iDsOptions: ANY STANDARD DATASET OPTIONS THAT YOU WOULD LIKE TO APPLY SUCH
** AS A WHERE STATEMENT.
** iQuote : (0=NO,1=YES). DEFAULT=0/NO. DETERMINES WHETHER THE RETURNED
** LIST IS QUOTED OR NOT.
** iQuoteChar: (SINGLE,DOUBLE) DEFAULT=SINGLE. SPECIFIES WHETHER SINGLE
** OR DOUBLE QUOTES ARE USED WHEN QUOTING THE RETURNED LIST
**
*****************************************************************************
** VERSION:
** 1.8 MODIFIED: 11-OCT-2010 BY: KN
** ALLOW BLANK CHARACTER VALUES AND ALSO REMOVED TRAILING
** ALLOW PARENTHESES IN CHARACTER VALUES
*****************************************************************************/
%macro ds2list(iDs=, iField=, iDsOptions=, iDelimiter=%str(,), iQuote=0, iQuoteChar=single);
%local dsid pos rc result cnt quotechar;
%let result=;
%let cnt=0;
%if &iQuote %then %do;
%if "%upcase(&iQuoteChar)" eq "DOUBLE" %then %do;
%let quotechar = %nrstr(%");
%end;
%else %if "%upcase(&iQuoteChar)" eq "SINGLE" %then %do;
%let quotechar = %nrstr(%');
%end;
%else %do;
%let quotechar = %nrstr(%");
%put WARNING: MACRO.DS2LIST.SAS: PARAMETER IQUOTECHAR INCORRECT. DEFAULTED TO DOUBLE;
%end;
%end;
%else %do;
%let quotechar = ;
%end;
/*
** ENSURE ALL THE REQUIRED PARAMETERS WERE PASSED IN.
*/
%if "&iDs" ne "" and "&iField" ne "" %then %do;
%let dsid=%sysfunc(open(&iDs(&iDsOptions),i));
%if &dsid %then %do;
%let pos=%sysfunc(varnum(&dsid,&iField));
%if &pos %then %do;
%let rc=%sysfunc(fetch(&dsid));
%do %while (&rc eq 0);
%if "%sysfunc(vartype(&dsid,&pos))" = "C" %then %do;
%let value = %qsysfunc(getvarc(&dsid,&pos));
%if "%trim(&value)" ne "" %then %do;
%let value = %qsysfunc(cats(%nrstr(&value)));
%end;
%end;
%else %do;
%let value = %sysfunc(getvarn(&dsid,&pos));
%end;
/* WHITESPACE/CARRIAGE RETURNS REMOVED IN THE BELOW LINE */
/* TO ENSURE NO WHITESPACE IS RETURNED IN THE OUTPUT. */
%if &cnt ne 0 %then %do;%unquote(&iDelimiter)%end;%unquote("echar&value"echar.)
%let cnt = %eval(&cnt + 1);
%let rc = %sysfunc(fetch(&dsid));
%end;
%if &rc ne -1 %then %do;
%put WARNING: MACRO.DS2LIST.SAS: %sysfunc(sysmsg());
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: FIELD &iField NOT FOUND IN DATASET %upcase(&iDs).;
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: DATASET %upcase(&iDs) COULD NOT BE OPENED.;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: YOU MUST SPECIFY BOTH THE IDS AND IFIELD PARAMETERS TO CALL THIS MACRO.;
%end;
%mend;