10-26-2017 02:42 PM - edited 10-26-2017 02:43 PM
Often I have a list of items:
%let list=Monday Tuesday Wednesday Thursday Friday Saturday;
And I want to remove n items from the list. So suppose I want to remove Tuesday and Thursday. You can do it with TRANWRD(), but if you are removing multiple words, it gets ugly fast if you nest them, and feels like it shouldn't need two function calls.
%let list2=%sysfunc(tranwrd(%sysfunc(tranwrd(&list,Tuesday,)),Thursday,)); %put &list2;
Sometimes when I have faced this I have written a little function-style macro to loop through the items of a list, checking to see if each item is in the list of items to remove. Something like:
%macro removeitem(list=,remove=); %local i item_i return ; %do i=1 %to %sysfunc(countw(&list,%str( ))); %let item_i=%scan(&list,&i,%str( )); %if %sysfunc(findw(&remove,&item_i))=0 %then %let return=&return &item_i; %end; &return %mend; %let list3=%RemoveItem(list=&list,remove=Tuesday Thursday); %put &list3;
Above approaches work, but I never feel good about them. I always assume there is a function I'm not thinking of, like a REMOVE() function, or one of the TRANSTRN/TRANWRD functions that would allow me to pass a list of strings to replace and a list of replacement strings, like TRANSLATE() allows.
Or maybe today's the day that I dive into regular expressions?
I think of this as a macro problem, because that's usually where I encounter such lists, but if there is a nifty data step solution, happy to see that, especially if in can be macrified (macrocized?)
10-27-2017 04:02 AM
10-26-2017 02:54 PM
Perhaps it's just a small piece of the puzzle, but you can simplify the list processing. Instead of accumulating the items into a list, just generate the "non-removed" items one word at a time. In this case:
%do i=1 %to %sysfunc(countw(&list,%str( )));
%let item_i=%scan(&list,&i,%str( ));
%if %sysfunc(findw(&remove,&item_i))=0 %then &item_i;
%let list3=%RemoveItem(list=&list,remove=Tuesday Thursday);
10-27-2017 04:02 AM
10-27-2017 08:52 AM
Thanks @s_lassen that's nifty approach.
I added a Ballot idea for a ReplaceW() function. Not sure if it's really feasible, but I think it's what I want.
10-27-2017 10:13 AM
@Quentin: While I agree that regular expressions are a lot more powerful and well worth learning, you can always simplify the task in a data step by using a do loop. Still uses multiple uses of the tranwrd function, but easier (for me at least) to read. e.g.:
data have (drop=i); informat days $70.; length i $10; input days &; do i='Monday','Wednesday','Friday'; days=tranwrd(days,i,''); end; cards; Monday Tuesday Wednesday Wednesday Friday Monday Tuesday Thursday Monday Sunday ; run;
Art, CEO, AnalystFinder.com
10-27-2017 01:17 PM - edited 10-27-2017 01:20 PM
Agree @art297, the looping structure is much easier on the eyes. Even more so in the macro setting. I was prompted to start this thread because I had started with one replacement, then two, then three. And ended up with to following atrocity in my code:
%let list=These SomeWordToRemove are SomeOtherWordToRemove the YetAnotherWordToRemove words to keep; %put %sysfunc(tranwrd(%sysfunc(tranwrd(%sysfunc(tranwrd(&list,SomeWordToRemove,)),SomeOtherWordToRemove,)),YetAnotherWordToRemove,)) ;
I promised myself I would take the time to refactor it into a little function-style macro that does looping as you illustrated. : )
Need further help from the community? Please ask a new question.