BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
CHELS
Obsidian | Level 7

I am trying to create a macro that change some words in a sting to lower case. However, only the first word in the list 'Or' was being changed.

 


%Macro PCase (Dat=, Var=, LowList=);

 

Data Cased;
set &Dat;

%let VarCnt=%sysfunc(countw(&LowList));
%do i = 1 %to &VarCnt;
%let Var_Ind = %scan(&LowList,&i," ");
X1 = tranwrd(X1,"&Var_Ind", lowcase("&Var_Ind"));
%end;
run;
%mend PCase;

 

 

%let Low = %str(Or For And And/Or );

%PCase (Dat=A, Var=Original, LowList=&Low);

 

1 ACCEPTED SOLUTION

Accepted Solutions
Patrick
Opal | Level 21

It's normally best to make everything fully working without macro code. Here how this could look like:

data Original;
  input X1 $60.;
  cards;
SURGERY FOR BLEEDING
DELAYED AND/OR MISSED EXAMINATIONS
REMOVAL OF STITCHES
;

data makeItWork;
  set original;
  x2=propcase(x1);
  if 0 then x3=x2;
  x3=prxchange('s/\b(and|or|for|of)\b/\l\1/oi',-1,trim(x2));
run;

Now that it's working make it dynamic using a macro.

options mprint;
%macro pcase (ds=, outds=, var=, outvar=, lowlist=);
  %if &outds= %then %let outds=&ds;
  %if &outvar= %then %let outvar=&var;
  data &outds;
    set &ds;
    if 0 then &outvar=&var;
    &outvar=propcase(&var);
    &outvar=prxchange("s/\b(&lowlist)\b/\l\1/oi",-1,trim(&outvar));
  run;
%mend pcase;

%let low = %str(and|or|for|of );
%pcase (ds=Original, var=x1, lowlist=&low);

proc print data=Original;
run;

Below line is just to assign the new variable with the same length than an already existing variable (function prxchange() would create the new variable with a default length of 200).

if 0 then x3=x2;

 

Capture.JPG

 

And once you've got a working macro you can make it even more dynamic like allowing for changing multiple variables at once. And like always: You kind of need to keep the balance as the more you add the more complicated the code becomes. Below macro would already need quite a bit of inline comment so that you (or someone else) still can understand it it the future.

 

data Original;
  input X1 $60.;
  x2=x1;
  cards;
SURGERY FOR BLEEDING
DELAYED AND/OR MISSED EXAMINATIONS
REMOVAL OF STITCHES
;

%macro pcase (ds=, outds=, var=, outvar=, lowlist=);
  %if &outds= %then %let outds=&ds;
  %if &outvar= %then %let outvar=&var;
  data &outds;
    set &ds;
    %do i=1 %to %sysfunc(countw(&outvar));
      if 0 then %scan(&outvar,&i)=%scan(&var,&i);
      %scan(&outvar,&i)=propcase(%scan(&var,&i));
      %scan(&outvar,&i)=prxchange("s/\b(&lowlist)\b/\l\1/oi",-1,trim(%scan(&outvar,&i)));
    %end;
  run;
%mend pcase;

%let low = %str(and|or|for|of );
%pcase (ds=Original, var=x1 x2, outvar=newX1 newX2, lowlist=&low);

proc print data=Original;
run;

 

 

 

View solution in original post

8 REPLIES 8
PaigeMiller
Diamond | Level 26

We don't have data set A so we can't run your code.

 

It's also unclear why you need to use a macro loop here rather than a data step loop (and in my mind unnecessary to use macros at all).

--
Paige Miller
CHELS
Obsidian | Level 7

Thank you for your response. The reason why I am using a macro is because there will be more data steps added inside the macro to modify the case of the variable. For example, keeping acronyms in capital letter etc.  Here is an example of my Original data with the variable named X1

 

Data Original:

input X1 $60;

cards;

SURGERY FOR BLEEDING

DELAYED AND/OR MISSED EXAMINATIONS

REMOVAL OF STITCHES

;

run;

 

%Macro PCase (Dat=, Var=, LowList=);

Data Cased;
set &Dat;
X2 = propcase (&Var);

%let VarCnt=%sysfunc(countw(&LowList));
%do i = 1 %to &VarCnt;
%let Var_Ind = %scan(&LowList,&i," ");
X3 = tranwrd(X2, "&Var_Ind", lowcase("&Var_Ind"));
%end;
run;
%mend PCase;

 

It works If I define the list as: %let Low = %str(Or),

                                             %PCase (Dat=Original, Var=X1, LowList=&Low);

it which change all the 'Or' to 'or'.

 

If I define the list as  %let Low = %str(Or For And/Or) then nothing works.

PaigeMiller
Diamond | Level 26

I still don't see the need for a macro, when all computations occur within a data step.

 

But since you provide an example ... you have upper case data, and you are searching for mixed case, and no matches are found.

 

--
Paige Miller
CHELS
Obsidian | Level 7

Thank you!

Patrick
Opal | Level 21

It's normally best to make everything fully working without macro code. Here how this could look like:

data Original;
  input X1 $60.;
  cards;
SURGERY FOR BLEEDING
DELAYED AND/OR MISSED EXAMINATIONS
REMOVAL OF STITCHES
;

data makeItWork;
  set original;
  x2=propcase(x1);
  if 0 then x3=x2;
  x3=prxchange('s/\b(and|or|for|of)\b/\l\1/oi',-1,trim(x2));
run;

Now that it's working make it dynamic using a macro.

options mprint;
%macro pcase (ds=, outds=, var=, outvar=, lowlist=);
  %if &outds= %then %let outds=&ds;
  %if &outvar= %then %let outvar=&var;
  data &outds;
    set &ds;
    if 0 then &outvar=&var;
    &outvar=propcase(&var);
    &outvar=prxchange("s/\b(&lowlist)\b/\l\1/oi",-1,trim(&outvar));
  run;
%mend pcase;

%let low = %str(and|or|for|of );
%pcase (ds=Original, var=x1, lowlist=&low);

proc print data=Original;
run;

Below line is just to assign the new variable with the same length than an already existing variable (function prxchange() would create the new variable with a default length of 200).

if 0 then x3=x2;

 

Capture.JPG

 

And once you've got a working macro you can make it even more dynamic like allowing for changing multiple variables at once. And like always: You kind of need to keep the balance as the more you add the more complicated the code becomes. Below macro would already need quite a bit of inline comment so that you (or someone else) still can understand it it the future.

 

data Original;
  input X1 $60.;
  x2=x1;
  cards;
SURGERY FOR BLEEDING
DELAYED AND/OR MISSED EXAMINATIONS
REMOVAL OF STITCHES
;

%macro pcase (ds=, outds=, var=, outvar=, lowlist=);
  %if &outds= %then %let outds=&ds;
  %if &outvar= %then %let outvar=&var;
  data &outds;
    set &ds;
    %do i=1 %to %sysfunc(countw(&outvar));
      if 0 then %scan(&outvar,&i)=%scan(&var,&i);
      %scan(&outvar,&i)=propcase(%scan(&var,&i));
      %scan(&outvar,&i)=prxchange("s/\b(&lowlist)\b/\l\1/oi",-1,trim(%scan(&outvar,&i)));
    %end;
  run;
%mend pcase;

%let low = %str(and|or|for|of );
%pcase (ds=Original, var=x1 x2, outvar=newX1 newX2, lowlist=&low);

proc print data=Original;
run;

 

 

 

CHELS
Obsidian | Level 7

Thank you so much! I really appreciate you taking the time to code and teach me!

Quentin
Super User

Agree with @PaigeMiller this is not really a macro problem.  But that said, your current code works when I run it on sample data.

 

I would check the case of your real data, as tranwrd is case-sensitive when searching for words to replace.

 

Here's your code, with just one value of data hard-coded.

 

%Macro PCase (Dat=, Var=, LowList=);

Data Cased;
x1="Long sentence with Or For And And/Or" ;

%let VarCnt=%sysfunc(countw(&LowList));
%do i = 1 %to &VarCnt;
%let Var_Ind = %scan(&LowList,&i," ");
X1 = tranwrd(X1,"&Var_Ind", lowcase("&Var_Ind"));
%end;

put x1= ;
run;
%mend PCase;

%let Low = %str(Or For And And/Or );
%PCase (Dat=A, Var=Original, LowList=&Low)

Returns the desired:

x1=Long sentence with or for and and/or
NOTE: The data set WORK.CASED has 1 observations and 1 variables.
The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.
CHELS
Obsidian | Level 7
Thank you!

sas-innovate-wordmark-2025-midnight.png

Register Today!

Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.


Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 8 replies
  • 1471 views
  • 2 likes
  • 4 in conversation