I need to output some array values that contain side calculations from a given data step to a separate file. It works right now using @ but the numbers are left justified. I can use macros to use the format I need but the character length of the numbers would have to be known before the data step is executed. each program has code that is one big data step, so I have to find the solution that works without extra data steps or proc statements.
these are always integers so I want to output them in n. format where n is the max character length of the number in standard form. I have an array with these values but I dont know of a way to use those values to get a format.
/* psudo code to iterate through array and print values. I have code for arbitrary dimensional dataset, I
it just has a lot of extra mess and the idea is the same. */
data example;
array saved_length {3} (3,2,1); /*contains max length for v/c/f for this given array */
%let vlen=3; %let clen=2; %let flen=1; *macro version of above;
file mprint;
do i =1 to 10;
put
"v= " &arrayname{i, 1} &vlen.. " " /* is there a way to use saved_length{i} */
"c= " &arrayname{i, 2} &clen.. " " /* value of x to get x. here? */
"f= " &arrayname{i, 3} &flen..; /* so I can replacing having to use &.len? */
end;
file LOG;
run;
I would like output of the form like this so the columns are aligned right
v= 1 c= 1 f =1
v= 21 c= 21 f= 2
v= 321 c= 21 f= 3
So you want to do something like:
put
@2 "d1= " d1 2.
@9 "d2= " d2 2.
@16 "d3= " d3 2.
@23 "V= " ExARRAY(d1,d2,d3,1) 4.
@30 "C= " ExARRAY(d1,d2,d3,2) 3.
@38 "F= " ExARRAY(d1,d2,d3,3) 2.
;
But with the 2,9,16,23,30,38 and 2,2,2,4,3,2 sets of numbers varying?
First question is why isn't it good enough to just figure out the max possible widths and always use those?
Looks like they are integers since you are not printing any decimal part. The maximum integer SAS can represent exactly (without having holes in the sequence) use 16 digits. So if you use 16,16,16 instead of 4,3,2 then any values in the EXARRAY will fit.
Assuming there is a good reason not to do that then just build a string variable and put that.
You haven't said how you are calculating the width to use but lets assume have done so and stored them in another array of length 3 called W.
put @2 "d1= " d1 2. @9 "d2= " d2 2. @16 "d3= " d3 2. @;
length string $200 ;
string = cat(' V= ',putn(ExARRAY[d1,d2,d3,1],cats(w[1],'.'))
,' C= ',putn(ExARRAY[d1,d2,d3,2],cats(w[2],'.'))
,' F= ',putn(ExARRAY[d1,d2,d3,3],cats(w[3],'.')))
;
string_len = length(string);
put string $varying200. string_len;
Why is what you posted not good enough?
The F format may be all that you need
data example; file print; input x; put "x=" x f5. +1 "y=" x F3. +1 "z=" x f8.; datalines; 1 11 111 ;
Be aware that your output destination can cause things to "miss-align" because of proportional fonts like this window uses. See the two lines below. Both 6 digits but the second like very likely appears longer. So your output may look shifted for anything that does not use a fixed space font.
111111
000000
You may need to use another approach if appearance is more important than actual numbers of spaces, such as a report procedure.
Hi Ballard
The font spacing doesn't matter to me, just the number of spaces needs to be consistent.
my problem is replacing the f5. f3. etc in your example with the max length if I can. like if the max length in the array is 3, then I would like f3. and if its 8 i would do f8. for that section.
I have the max values stored, but I dont know how to turn an array value of 7 -> f7. for max length in your example.
if it doesn't seem possible to do its not the end of the world, I just figure with everything else we can do it seems like there should be something like put_integer(x, *numeric value*) to pass an integer as a numeric format.
@weg wrote:
Hi Ballard
The font spacing doesn't matter to me, just the number of spaces needs to be consistent.
my problem is replacing the f5. f3. etc in your example with the max length if I can. like if the max length in the array is 3, then I would like f3. and if its 8 i would do f8. for that section.
I have the max values stored, but I dont know how to turn an array value of 7 -> f7. for max length in your example.
if it doesn't seem possible to do its not the end of the world, I just figure with everything else we can do it seems like there should be something like put_integer(x, *numeric value*) to pass an integer as a numeric format.
Macro variables are text. If you have one you can use it in the F format.
%let l1=5; %let l2=3; %let l3=8; data example; file print; input x; put "x=" x f&L1.. +1 "y=" x F&L2.. +1 "z=" x f&L3..; datalines; 1 11 111 ;
You need 2 dots because the dot is the macro processor concatenate and/or end of macro value indicator. Since format references require a dot to generate F3. you need the dot in the macro reference: F&L1..
Second, "length" and numeric values needs to be discussed carefully in SAS. The "length" of numeric variable ranging from 3 to 8 is an expression of the number of bytes used to store the value and is only very loosely related to display. If you mean "number of digits" that is a better way to phrase things.
I still don't get what you are doing and how ARRAY's get into it.
If you need to find a width to use for a variable you probably just want to calculate the maximum width needed for any value. For integers that is going to be the width needed for the maximum value (or the minimum if your numbers include negative values).
data have;
input x @@ ;
cards;
1 1234 -34567
;
proc sql noprint;
select max(length(strip(put(x,32.)))) into :xlen trimmed
from have
;
quit;
data _null_;
set have;
put 'X=' x &xlen.. ;
run;
Results:
11 data _null_; 12 set have; 13 put 'X=' x &xlen.. ; 14 run; X= 1 X= 1234 X=-34567
If you need to make the format width vary then perhaps you want to use PUTN() function to generate a string and then use $VARYING format to print it.
data have;
input x w @@ ;
cards;
1 3 1234 5 -34567 7
;
data _null_;
set have;
str = putn(x,cats(w,'.'));
put 'X=' str $varying200. w ;
run;
Results:
30 data _null_; 31 set have; 32 str = putn(x,cats(w,'.')); 33 put 'X=' str $varying200. w ; 34 run; X= 1 X= 1234 X= -34567
Note: The F format is just another name for the regular numeric format used in my code above.
@Tom wrote:
I still don't get what you are doing and how ARRAY's get into it.
I am writing the arrays to a file which are snapshots of key variables in the data process used for logging. these are side calculations, IE I alrady am outputting multiple other datasets in the data statement I might need to output the same array multiple times at different stages and the lengths would be different so it is a lot easier to use mprint and put to output than to save them as a ton of datasets and output in another step, which is why I am trying to get a solution that works for the given data step.
here is another example of an array I would need to print that has multiple dimensions. I got to print like 10-20 of these in a single data step, and they are not the main output which is why I really cant use multiple data or proc steps
file mprint;
put "ExARRAY(EX1,EX2,EX3,EX4)";
put "**************************";
do d1=1 to 2;
do d2 =1 to 2;
do d3 =1 to 7;
put @2 "d1= " d1 2. @9 "d2= " d2 2. @16 "d3= " d3 2.
@23 "V= " ExARRAY(d1,d2,d3,1) 4.
@30 "C= " ExARRAY(d1,d2,d3,2) 3.
@38 "F= " ExARRAY(d1,d2,d3,3) 2.;
end;
end;
end;
file LOG;
I already can take the array name and variables as input: %print_array(array=Exarray, dimnames= ex1 ex2 ex3 ex4) and recreate this output code, including the @ placement. What I cannot recreate is the formatting. I have the lengths (for this example 2. 2. 2. 2. 4. 3. 2.) for each array as an integer in another array like in my example, but there is no way I know of in sas to make a format from that aside from macros, which doesnt work since they resolve before I can calculate the needed length for each array, which means I'm stuck doing what I currently do an manually specify the format lengths.
So you want to do something like:
put
@2 "d1= " d1 2.
@9 "d2= " d2 2.
@16 "d3= " d3 2.
@23 "V= " ExARRAY(d1,d2,d3,1) 4.
@30 "C= " ExARRAY(d1,d2,d3,2) 3.
@38 "F= " ExARRAY(d1,d2,d3,3) 2.
;
But with the 2,9,16,23,30,38 and 2,2,2,4,3,2 sets of numbers varying?
First question is why isn't it good enough to just figure out the max possible widths and always use those?
Looks like they are integers since you are not printing any decimal part. The maximum integer SAS can represent exactly (without having holes in the sequence) use 16 digits. So if you use 16,16,16 instead of 4,3,2 then any values in the EXARRAY will fit.
Assuming there is a good reason not to do that then just build a string variable and put that.
You haven't said how you are calculating the width to use but lets assume have done so and stored them in another array of length 3 called W.
put @2 "d1= " d1 2. @9 "d2= " d2 2. @16 "d3= " d3 2. @;
length string $200 ;
string = cat(' V= ',putn(ExARRAY[d1,d2,d3,1],cats(w[1],'.'))
,' C= ',putn(ExARRAY[d1,d2,d3,2],cats(w[2],'.'))
,' F= ',putn(ExARRAY[d1,d2,d3,3],cats(w[3],'.')))
;
string_len = length(string);
put string $varying200. string_len;
What's wrong with a report procedure? You can specify alignment in most.
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.