Hello
I am using SAS9.4 on a Windows10 PC environment.
I am trying to create a string column to contain a full description by using a format within a PUT statement. I want the format name to be generated dynamically based on a value in a different column. This seems feasible but I can't quite nail it, example program attached.
I have this problem on a couple of programs so all help will be gratefully received, thank you.
/* T1002580 Using putc or putn with format names in your data
Using putc or putn with format names in your data
This seems to work
inspired by
https://goo.gl/ktxY1O
https://communities.sas.com/t5/Base-SAS-Programming/Dynamic-apply-format-by-making-the-fmtname-from-a-value-in-the/m-p/335244
HAVE
====
proc format;
value $ FMTA1X
"A" = "this was discount $10"
"B" = "this was discount $20"
;
value $ FMTA2X
"A" = "this was discount $300"
"B" = "this was discount $400"
;
run;
Up to 40 obs WORK.HAVE total obs=5
Obs PRODTYPE DISCOUNT
1 A1 B
2 A2 A
3 A2 A
4 A2 B
5 A1 B
WANT
====
Up to 40 obs WORK.DEMO_DESCRIPTION_HARDCODE total obs=5
Obs PRODTYPE DISCOUNT DISCOUNT_DESC
1 A1 B this was discount $20 putc(discount,$fmta1x.)
2 A2 A this was discount $300
3 A2 A this was discount $300
4 A2 B this was discount $400
5 A1 B this was discount $20
SOLUTION
========
data have;
input Prodtype $ Discount $;
cards4;
A1 B
A2 A
A2 A
A2 B
A1 B
;;;;
run;quit;
data want;
retain prodtype discount discount_desc;
length discount_desc $44;
set have;
fmt=cats('$fmt',prodtype,'x');
Discount_desc = putc(Discount,fmt);
run;quit;
Up to 40 obs WORK.WANT total obs=5
Obs PRODTYPE DISCOUNT DISCOUNT_DESC FMT
1 A1 B this was discount $20 $fmtA1x
2 A2 A this was discount $300 $fmtA2x
3 A2 A this was discount $300 $fmtA2x
4 A2 B this was discount $400 $fmtA2x
5 A1 B this was discount $20 $fmtA1x
Not possible.
The format is set when the data step is compiled, so formats need to be named literally.
From the documentation of the put() function:
format.
contains the SAS format that you want applied to the value that is specified in the source. This argument must be the name of a format with a period and optional width and decimal specifications, not a character constant, variable, or expression.
(Emphasis by me)
Read the documentation.
Post your examples as text in a code window ({i} above post). Is there a reason you want to try to automate it, you will need to have the list somewhere if so. If its just a couple then what you have is fine. To automate will take more code:
data fmts; length fmt_cond fmt $200; input fmt_cond $ fmt $; datalines; A1 $fmt2 A2 $fmt1 ; run; data _null_; set fmts end=last; if _n_=1 then call execute('data want; set have;'); call execute('if variable="'||strip(fmt_cond)||'" then res=put( ...); if last then call execute('run;'); run;
Just as an example, however I would really question why this is necessary at all.
PUTC/PUTN allow format names that are from a variable.
Borrowing code:
data fmts;
length fmt_cond fmt $200;
input fmt_cond $ fmt $;
datalines;
A1 $fmt2
A2 $fmt1
;
run;
Data want;
set fmts;
want_var = putc(fmt_cond, fmt);
Run;
I suspect you would also be well served by a hash lookup table, keyed on 2 vars: PRODTYPE and DISCOUNT. Make a lookup dataset, with all the expected combinations of prodtype,discount, and discount_desc. Then load it into a hash object to use in the data step:
data lookup;
input prodtype :$2. discount :$1. @6 discount_desc $30.;
datalines;
A1 A this was discount $10
A1 B this was discount $20
A2 A this was discount $300
A2 B this was discount $400
run;
data demo_description_dynamic (drop=rc);
set demodata;
if _n_=1 then do;
if 0 then set lookup;
declare hash h (dataset:'lookup');
h.definekey('prodtype','discount');
h.definedata(all:'Y');
h.definedone();
end;
rc=h.find();
if rc^=0 then call missing(discount_desc);
run;
When the h.find() method is successful, two things happen: RC=0 and discount_desc is retrieved from the desired item in hash object h. If it's unsuccessful, RC ^=0 and discount_desc is unchanged from the last successful h.find(). That's why you see discount_desc set to missing when rc^=0.
/* T1002580 Using putc or putn with format names in your data
Using putc or putn with format names in your data
This seems to work
inspired by
https://goo.gl/ktxY1O
https://communities.sas.com/t5/Base-SAS-Programming/Dynamic-apply-format-by-making-the-fmtname-from-a-value-in-the/m-p/335244
HAVE
====
proc format;
value $ FMTA1X
"A" = "this was discount $10"
"B" = "this was discount $20"
;
value $ FMTA2X
"A" = "this was discount $300"
"B" = "this was discount $400"
;
run;
Up to 40 obs WORK.HAVE total obs=5
Obs PRODTYPE DISCOUNT
1 A1 B
2 A2 A
3 A2 A
4 A2 B
5 A1 B
WANT
====
Up to 40 obs WORK.DEMO_DESCRIPTION_HARDCODE total obs=5
Obs PRODTYPE DISCOUNT DISCOUNT_DESC
1 A1 B this was discount $20 putc(discount,$fmta1x.)
2 A2 A this was discount $300
3 A2 A this was discount $300
4 A2 B this was discount $400
5 A1 B this was discount $20
SOLUTION
========
data have;
input Prodtype $ Discount $;
cards4;
A1 B
A2 A
A2 A
A2 B
A1 B
;;;;
run;quit;
data want;
retain prodtype discount discount_desc;
length discount_desc $44;
set have;
fmt=cats('$fmt',prodtype,'x');
Discount_desc = putc(Discount,fmt);
run;quit;
Up to 40 obs WORK.WANT total obs=5
Obs PRODTYPE DISCOUNT DISCOUNT_DESC FMT
1 A1 B this was discount $20 $fmtA1x
2 A2 A this was discount $300 $fmtA2x
3 A2 A this was discount $300 $fmtA2x
4 A2 B this was discount $400 $fmtA2x
5 A1 B this was discount $20 $fmtA1x
Thank you all for your contributions.
The other way I was looking at the problem was to use create a function (Proc FCMP) which would have been stored in one place and then reference across the different programs.
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!
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.