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
I'm a bit confused.
Your code define Fn as values, not variables?
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 ⦥
then in your datastep:
if D(i) in (&range.) then Flag=1;
- Cheers -
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;
@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 -
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);
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.
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 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.