Tranwrd just one letter in SAS - sas

How can i quote just one letter in sas?
%sysfunc(tranwrd(%quote(&string),%quote(T),%quote('Test')));
The Problem is, when the string has a 'T' and 'TR' that both get tranwrd to 'Test'

SAS Macro variables are always character. The arguments to macro functions are always character and generally won't require an extra layer of macro macro quoting, and definitely won't if the arguments are to be as literals.
Did you try this first ?
%let string = STACKOVERFLOW;
%let string_tweaked = %sysfunc(tranwrd(&string),T,Test);
%put NOTE: string_tweaked = &string_tweaked;
Do the macro values contain embedded single quotes ?
%let string = %str(S%'T%'ACKOVERFLOW);
%let string_tweaked = %qsysfunc(tranwrd(&string,'T','Test'));
%put NOTE: string_tweaked = &string_tweaked;
The second code sample is analogous to the following DATA step code (whose scope is different than that of the MACRO environment). DATA step string values are explicitly quoted, with either double quote (") or single quote (')
data _null_;
string = "S'T'ACKOVERFLOW";
string_tweaked = tranwrd(string,"'T'","'Test'");
put "NOTE: " string_tweaked=;
run;

Related

SAS: Delete missing items in a macro variable

I have a macro variable called list1, which gives:
%put &list1.;
A,B,C,D,,,G,,I,,
I need to delete those items which are missing i.e empty between the commas. The final output should be like A,B,C,D,G,I. Is it possible? Trim function does not seem to work here. I'm new to SAS so any help will be appreciated here. Thanks.
%let a= A,B,C,D,,,G,,I,,;
data c;
val = tranwrd(trim(compbl(tranwrd("&a",","," "))), " ",",");
call symput('b',val);
run;
%put &b;
This result of A,B,C,D,G,I
Just for fun - pure macro regex version:
%let a= A,B,C,D,,,G,,I,,;
%let regex1 = %sysfunc(prxparse(%str(s/,+/,/)));
%let b=%sysfunc(prxchange(&regex1,-1,%quote(&a)));
%let regex2 = %sysfunc(prxparse(%str(s/,$//)));
%let b=%sysfunc(prxchange(&regex2,-1,%quote(&b)));
%put b= &b;
You can skip the second regex if you never have to deal with trailing commas at the end of your input.
You could convert the commas to spaces and use COMPBL() to collapse the multiple spaces. Then convert the spaces back into commas. In case you have any spaces just swap the comma and the space characters.
%let temp=%sysfunc(translate(%superq(list1),%str( ,),%str(, )));
%let temp=%sysfunc(compbl(%superq(temp)));
%let list2=%sysfunc(translate(%superq(temp),%str( ,),%str(, )));
PS You don't normally want to use comma as the delimiter in macro variables. It makes it difficult to pass the value to other macros or functions since comma is used to separate argument values. Hence all of the macro quoting functions in this code. For variable lists you would normally want a space delimited list where multiple adjacent spaces wouldn't matter. For other types of lists you would normally want another delimiter like | or ^.

Can input function take a macro variable as character argument?

SAS says: ERROR: INPUT function requires a character argument.
when I run the following code:
%let fyq0 = 000930 ;
%put &fyq0 ;
proc sql ;
create table check as
select *, input(&fyq0,yymmdd6.) as fyq0
from q ;
quit ;
I tried to have '&fyq0' instead of &fyq0 as the 1st argument for the -input- function as the following:
%let fyq0 = 000930 ;
%put &fyq0 ;
proc sql ;
create table check as
select *, input('&fyq0',yymmdd6.) as fyq0
from q ;
quit ;
Then SAS says: NOTE: Invalid argument to function INPUT. Missing values may be generated. And indeed, variable fyq0 returns a missing value.
I wonder what went wrong with my approaches and what is the correct way to go.
The macro processor just replaces the macro reference with the resolved text and then the generated text is interpreted as text. So when you tried.
input(&fyq0,yymmdd6.)
it was the same as if your code was
input(000930,yymmdd6.)
Even if that did run SAS would first have to convert the number 930 into a character string using the BEST12. format which would result in a string like
' 930'
and the input function would only read 6 of the leading spaces.
The macro processor does not process text inside of single quotes. So the INPUT function could not convert the five character string '&fyq0' to a valid date since the letters and ampersand are not valid digits.
You can use double quote characters to allow SAS to resolve the macro variable reference.
input("&fyq0",yymmdd6.)
Now the macro variable reference will resolve and code generated will be:
input("000930",yymmdd6.)

Length of a SAS macro variable that contains quotes and commas

Let the sample macro variable be
%let temp="A","B","C";
How do you get the array length of this macro variable, which includes quotations and commas? For example, I would want to return length 3.
%let length_of_temp=%sysfunc(SOME_FUNC(&temp.));
%put length=length_of_temp;
LOG: length=3
Preferably I would want to do it using one established SAS function or line of code, not creating a new function for processing. Here is what I have attempted so far:
countw("&temp.",","): the quotes create an error when trying to convert it to a string.
NOTE: Line generated by the macro variable "TEMP". 4
""A","B","C"
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.
ERROR 388-185: Expecting an arithmetic operator.
countw(&temp.,",") and count(&temp.): typical error of function call has too many arguments
count((&temp.)) and dim((&temp.))
variations using %superq on the above
Use macro quoting on your macro variable value so that commas do not cause trouble for call to countw() function. Use the q and possibly the m optional third argument to the countw() function to let it know not to count delimiters that are inside the quotes.
%let temp="A1,a2","B","C";
%let count = %sysfunc(countw(%superq(temp),%str(,),mq));
If you want to calculate the count in a data step then instead of macro quoting you can use the symget() function to retrieve the value of the macro variable.
count = countw(symget('temp'),',','mq');
THis works for me:
%let temp="A","B","C";
%let count = %sysfunc(countw("&temp", ","));
%put Number of elements = &count.;
Results:
8002 %put Number of elements = &count.;
Number of elements = 3
The %quote function might be helpful here to mask the quotation marks:
%let count = %sysfunc(countw(%quote(&temp), ","));
%put Number of elements = &count.;

what the last "." after the macro variable mean

Below is the code to create the dataset from exisitng datasets, where end and end8, end7 are macro variables, just wondering why add . at the end of the macro variables?
data tab4a_&end.;
set
mck_tab4a_&end8.
mck_raw.mck_tab4a_&end7.
run;
The dot marks the end of the macro variable. It is often used when macro text is combined with static text, e.g. in a filename, so that SAS knows where the macro variable ends. E.g.:
%let year=2017;
%let filename = &year._accounts.xlsx;
%put &filename;
Produces 2017_accounts.xlsx
Without the first dot, SAS will produce a warning message, because it will be looking for a macro variable called year_accounts. (It can't tell where the macro ends and the text starts).
If there is a space or an end of statement after the macro variable then the dot can be omitted. Including the dot has no effect in this case. Some people think it is good to always include the dot.
It's used to denote the end of a macro variable. This is important when you're mixing macro variables and text.
For example:
%let my_var = TEST;
%let my_var_new = WRONG;
%put &my_var._new;
%put &my_var_new.;
OUTPUT:
58 %let my_var = TEST;
59 %let my_var_new = WRONG;
60 %put &my_var._new;
TEST_new
61 %put &my_var_new.;
WRONG
How does the macro processor know if the macro variable is my_var or my_var_new? The period tells SAS where the macro variable ends.
Including the dot also turns the macro variable green in the editor which helps for reading and debugging code.

SAS: Break up long string in code

I find it good practice to restrict my code to within 80 characters per line. Since SAS ignores white space, this usually isn't a problem. However, I occasionally need to refer to some string which is excessively long.
For example,
filename infile "B:\This\file\path\is\really\long\but\there\is\nothing\I\can\do\about\it\because\it\is\on\a\shared\network\drive\and\I\am\stuck\with\whatever\organization\or\lack\thereof\exists\for\directory\hierarchies\filename.txt";
I can think of two solutions:
1) Insert a carriage return. This however makes the code look quite messy and may unwittingly introduce invisible characters (i.e \r\n) into the string.
filename infile "B:\This\file\path\is\really\long\but\there\is\nothing\
I\can\do\about\it\because\it\is\on\a\shared\network\drive\and\I\am\stuck\
with\whatever\organization\or\lack\thereof\exists\for\directory\hierarchies\
filename.txt";
2) Use macro variables to break the string into several parts.
%let part1 = B:\This\file\path\is\really\long\but\there\is\nothing\;
%let part2 = I\can\do\about\it\because\it\is\on\a\shared\network\drive\and\I\am\stuck\;
%let part3 = with\whatever\organization\or\lack\thereof\exists\for\directory\hierarchies\;
%let part4 = filename.txt;
filename infile "&part1.&part2.&part3.&part4.";
%let path = %sysfunc(pathname(infile));
%put &path;
Ideally, I would like something which allows me to follow the indentation scheme of the rest of the code.
filename infile "B:\This\file\path\is\really\long\but\there\is\nothing\
I\can\do\about\it\because\it\is\on\a\shared\network\drive\and\I\am\stuck\
with\whatever\organization\or\lack\thereof\exists\for\directory\hierarchies\
filename.txt";
A possible solution, at least within the context of this example, would be to bypass a declaration altogether and prompt the use for the input file. This does not appear easy to implement, however.
For this type of situation where the string needs to be used as one token then splitting it into separate macro variables is the best approach.
%let basedir=b:\Main Folder;
%let project=This project\has\many\parts;
%let fname=filename.txt ;
...
infile "&basedir/&project/&fname" ;
Note that SAS is happy to convert your directory delimiters between Unix (/) and Windows (\) style automatically for you.
You could also take advantage of using a fileref to point to a starting point in your directory tree.
filename basedir "&basedir";
...
infile basedir("&project/&fname");
You could also store the path in a text file or dataset and use that to generate the path into a macro variable.
data _null_;
infile 'parameter_file.txt' ;
input filename :$256. ;
call symputx('filename',filename);
run;
...
infile "&filename" ;
Another variation on using macro variable is to use multiple %LET statements to initialize a single macro variable. That way you can break the long string into multiple tokens.
%let fname=B:\This\file\path\is\really\long\but\there\is\nothing;
%let fname=&fname\I\can\do\about\it\because\it\is\on\a\shared\network\drive\and\I\am\stuck;
%let fname=&fname\with\whatever\organization\or\lack\thereof\exists\for\directory\hierarchies;
%let fname=&fname\filename.txt;
Or you could use a DATA step to set your macro variable instead.
data _null_;
call symputx('fname',catx('\'
,'B:\This\file\path\is\really\long\but\there\is\nothing\I\can'
,'do\about\it\because\it\is\on\a\shared\network\drive\and\I\am\stuck'
,'with\whatever\organization\or\lack\thereof\exists\for\directory'
,'hierarchies\filename.txt'
));
run;
For a situation where you need to put a long string in code such as a dataset label or some type of description consider using %cmpres. The function has limits but is useful to keep one inside 80 columns if they can use it. Here, my CR and other adjacent white spaces are being "compressed" in to a single space character.
%macro get_filename(FILEPATH_FILE, FILE)
/DES=%cmpres("returns a file's name, placed into var FILE, removing the
file path from FILEPATH_FILE.");
If you do this a lot, use %SYSFUNC() and COMPRESS() to make a user-defined macro like this:
%macro c(text);
%sysfunc(compress(&text, ,s))
%mend;
filename infile %c("B:\This\file\path\is\really\long\but\there\is\nothing\I\
can\do\about\it\because\it\is\on\a\shared\network\drive\
and\I\am\stuck\with\whatever\organization\or\lack\thereof\
exists\for\directory\hierarchies\and\he\uses\B\as\a\drive\
OMG\who\does\that\filename.txt");
%put %c("B:\This\file\path\is\really\long\but\there\is\nothing\I\
can\do\about\it\because\it\is\on\a\shared\network\drive\
and\I\am\stuck\with\whatever\organization\or\lack\thereof\
exists\for\directory\hierarchies\and\he\uses\B\as\a\drive\
OMG\who\does\that\filename.txt");
Option "s" in the COMPRESS() function removes all whitespace characters.
SAS posts notes on the log, you can ignore them:
NOTE: The quoted string currently being processed has become more than 262 characters long. You might have unbalanced quotation marks.