BookmarkSubscribeRSS Feed
CynthiaWei
Obsidian | Level 7

Hi SAS Pro,

I have a project that needs to use Array to create a new variable Flag as long as D01 to D30 has any of F01, F02, F03, F04...F98, or F99 (F01-F99 except F20-F29, 89 different values). Is there an easy way to put F01-F19 and F30-F99 all at once in a single array statement?

 

data want;

set have;

array D(30) D01-D30;

do i=1 to 30;

if D(i) in ('F01', 'F02', 'F03', 'F04', 'F05'..., 'F19', 'F30', 'F31', ...'F99') then Flag=1;

end;

drop i;

run;

 

I appreciate any help with this code!

Best regards,

C

12 REPLIES 12
LinusH
Tourmaline | Level 20

I'm a bit confused.

Your code define Fn as values, not variables?

Data never sleeps
CynthiaWei
Obsidian | Level 7
Hi LinusH,

You are right! F01-F99 except F20-F29 are all values/texts not variable. In another word, if any of the 30 variables D01-D30 has any of the texts of F01-F19 or F30-F99, then the new variable Flag=1, otherwise Flag=.

Thank you!
C
Oligolas
Barite | Level 11

Hi,

I'd probably determine the values in macro like this:

%macro loop();
   %local value i;
   %let value="F01";
   %do i=2 %to 99;
      %if not (20<=&i. and &i.<=29) %then %let value=&value. "F%sysfunc(putn(&i,z2.))"; 
   %end;
   &value.
%mend loop;

%let range=%loop();
%put &range;

then in your datastep:
if D(i) in (&range.) then Flag=1;
________________________

- Cheers -

CynthiaWei
Obsidian | Level 7
Hi,
Thank you so much for the code!
So, F01-F99 except F20-F29 are all values/texts not variable. I put them in a single quote to indicate they are array elements. If any of the 30 variables D01-D30 has any of the texts of F01-F19 or F30-F99, then the new variable Flag=1, otherwise Flag=.

From you above code, how can I decide the range, which are the variables D01-D30, that I want to look at and program.

Thank you!
CynthiaWei
Obsidian | Level 7
To better clarify, I just don't want to manually type in the texts from F01-F19 and F30-F99 individually in the array element section. 🙂
Reeza
Super User
Can you separate the text and number portion and do the comparison that way?
Or if these are diagnosis codes, make custom formats for diseases in a lookup table instead.
PeterClemmensen
Tourmaline | Level 20

Question: If just 1 of the 30 array elements are among the 89 values, you want flag = 1, right? regardless of whether any of the other values are equal to say, 'F20'?

 

Character ranges are tricky, but if you data is representable, then you can simplify this with prx pattern logic and do something like

 

data want;
   set have;
   array D(30) D01 - D30;
   do i=1 to 30;
      if prxmatch('/F[013-9]\d/', D(i)) then Flag = 1;
   end;
   drop i;
run;
Oligolas
Barite | Level 11

@PeterClemmensen nice. Please note that you'll need start and end of string anchors to avoid matching 'AF10' or 'F999' for example.

@CynthiaWei I'm not sure if it is intended but by looping like this you always overwrite your flag with the examination of D30, you may like to reformulate more precisely what you intend to do. Shall all values D01-D30 match the requirement or just any of them?

________________________

- Cheers -

CynthiaWei
Obsidian | Level 7
Hi,

Thank you so much for your code. Does the statement '/F[013-9]\d/' can include all the texts from F01-F19 and F30-F99?

I know the programming code for my project is not difficult. I just don't want to manually type in the texts from F01-F19 and F30-F99 individually in the array element section.

Thank you!
Tom
Super User Tom
Super User

If  (and that is probably a big IF) the DX codes are well formed such that they follow the pattern of your example then you CAN use inequality tests on the values.

 

It is more efficient to set the flag to 0 (false) or 1 (true) and stop the loop as soon as it becomes true.

data want;
  set have;
  array d d01-d30;
  do index=1 to 30 until(flag);
    flag = ('F01' <= d[index] <= 'F19') or ('F30' <= d[index] <= 'F99');
  end;
  drop index;
run;

If the values can be things like 'D1x' then you probably cannot use the inequality tests.

 

If the values of the dx variables are messy then make the test more specific.

  flag = d[index]=:'F' and input(substr(d[index],2),??2.) in (1:19 30:99);
ballardw
Super User

One approach that can be quite flexible is to move the logic and values into a Format or Informat depending on usage.

I didn't type out all the values, the pattern should be obvious.

 

proc format;
value $Fcodeflag
'F01','F02','F03' = "in list"

run;

data want;
   set have;
   array D(*) D01-D30;
   do i= 1 to dim(d);
       if put(d[i],$fcodeflag.)= 'inlist' then flag=1;
   end;
   drop i;
run

Some advantages of a format approach:

 

1) one place to look for such and modify if you keep code organized

2) if the values are in a data set you can use code to turn the values into a format. So adding a group of values to the set and running code may be simpler than modifying the places where you might use the list.

 

Note the use of the (*) instead of 30 in the Array statement. With a list of variables the * will mean "get the size from the list". So if you add or remove variables the size adjusts to the provided variables.

The DIM instruction for the loop control. Dim returns the defined size of the array. So with the (*) in the array any change to list of variables also adjusts the loop counter.

 

Also if you only want to find set the flag one time, goes to code efficiency you can execute a LEAVE instruction to quit the loop as soon as the first match is found:

data want;
   set have;
   array D(*) D01-D30;
   do i= 1 to dim(d);
       if put(d[i],$fcodeflag.)= 'inlist' then do;
           flag=1;
           leave;
       end;
   end;
   drop i;
run

Which may a second use if you do not drop the i variable the value would be the index of the array where the value is found.

Might be useful in some cases.

Reeza
Super User
  • A data step to create the list
  • SQL to create the macro variable
  • Use in code
data list_codes;
	length code $ 3;

	do i=1 to 19, 30 to 99;
		code=catt('F', put(i, z2.));
		output;
	end;
run;

proc sql noprint;
	select quote(code, "'") into :code_list separated by ", " from list_codes;
quit;

%put &code_list;

data want;
	set have;
	array D(30) D01-D30;

	do i=1 to 30;

		if D(i) in (&code_list) then
			Flag=1;
	end;
	drop i;
run;

Another option I sometimes use when generating manual lists is to use Excel. 

I've attached an example of how that works and then I copy/paste it in the code. 

Issue with this, if you ever need to change it you usually need to rebuild it from scratch (which is quick) but annoying. Rarely an issue when you're hard coding lists like these though. 

 

 

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 12 replies
  • 1982 views
  • 0 likes
  • 7 in conversation