BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
zkreflex
Calcite | Level 5

So I need to write a macro that replicates this when executed with the appropriate arguments:

data_null_ ;

set books.list;

file "list3.data";

put @1 bookid 4. @5 author $30. @35 isbn isbn18. @43 yearpub comma6.;

run;

The macro is %macro writeoutC (dataset,datafile,variables,formatlist);

Appropriate arguments are %writeoutC (books.list,list3.data,bookid author isbn yearpub,4. $30. isbn18. comma6.);

As you can see the numbers after the @ are a running total of the numbers in the formatting << this is what is giving me trouble.

Here is what I have at the moment

%macro writeoutC (dataset,datafile,variables,formatlist);

%let newput= ;

%let newpointer= ;

%do i=1 %to 4;

%let newpointer=%sysfunc(scan(&formatlist,&i,' $',ap));

%let newput=&newput @%sysfunc(sum(&newpointer+%sysfunc(scan(&formatlist,&i,' $',ap)))) %sysfunc(scan(&variables,&i,'

')):%sysfunc(scan(&formatlist,&i,' '));

%end;

data _null_;

set &dataset;

file "&datafile";

put &newput;

run;

%mend writeoutC;

Which is obviously not working...

1 ACCEPTED SOLUTION

Accepted Solutions
data_null__
Jade | Level 19

OK I fixed your program, so that it produces this confusing mixture of pointer controlled put and list put that would be more easily accomplished almost any other way like adding -L format modifier to each format so that you don't need the : or @col.

@1 bookid:4. @5 author:$30. @35 isbn:isbn18. @53 yearpub:comma6.

%macro writeoutC (dataset,datafile,variables,formatlist);
  
%local newput i newpointer;
  
%do i=1 %to 4;
     
%if &i=1
        
%then %let newpointer=1;
         %else %let newpointer=%eval(&newpointer+%sysfunc(scan(&formatlist,%eval(&i-1),$str( $),ap)));
      %let newput=&newput @&newpointer %sysfunc(scan(&variables,&i,%str( ))):%sysfunc(scan(&formatlist,&i,%str( )));
      %end;

  
%put NOTE: %superq(newput);
  
/*
   data _null_;
   set &dataset;
   file "&datafile";
   put @1 &newput;
   run;
   */

  
%mend writeoutC;

/*Part 10*/

%
writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);


52        
53         %writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);
NOTE: @1 bookid:4. @5 author:$30. @35 isbn:isbn18. @53 yearpub:comma6.
54        
55        

View solution in original post

19 REPLIES 19
Kurt_Bremser
Super User

If you use formatted output, and you have no whitespace/delimiter, you don't need the position indicator.

And consider to use a data_null step for the creation of your newput macro variable. You then have the full flexibility of the data step at hand. Also use "do until" logic for your list of variables/formats (flexibilty!) and check that both lists have the same number of items.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Darn, ninja'd.  Was just writing this, same thing as KurtBremser mentioned, use datasets rather than parameter lists (i.e. imagine having to deal with datasets with hundreds of variables):

data template;
  length var_name pos var_fmt $200;
  infile datalines;
  input var_name pos var_fmt;
datalines;
NAME  @1  $8.
SEX   @9  $1.
;
run;

data _null_;
  set template end=last;
  if _n_=1 then call execute('data _null_; set sashelp.classfit; file="s:\temp\rob\classfit.txt"; put ');
  call execute(strip(pos)||' '||strip(var_name)||' '||strip(var_fmt));
  if last then call execute('; run;');
run;

data_null__
Jade | Level 19

You don't need to parse anything as long as the format list and name list have the same number of words.

The syntax you would use is.

put (variable-list)(format-list);

put (bookid author isbn yearpub)(4. $30. isbn18. comma6.);


put (&vars)(&formats);


As mentioned by you don't need to use column position @n syntax.

data_null__
Jade | Level 19

Perhaps a mildly interesting twist on the problem (using my second favorite CALL routine) would be to let SAS do most of the work.  You could have a parameter (VARS) for the variable list and another to modify any formats (FMTS) a parameter that has the same syntax as the FORMAT statement.  Then there is no need for synchronized lists.  You can also create a SAS data set that contains information that describes the record layout of the file.  The result with default file attributes are fixed length delimited records.

%let data=sashelp.shoes;
%let vars=Region Inventory Product Returns;
%let fmts=Region $12.-r Returns dollar12.2;

filename FT66F001 '~/example.dat';
data recordlayout(keep=_name_ _vtype_ _w_ _fmt_ _col_);
   retain &vars;
   length _NAME_ $32 _VTYPE_ $1 _COL_ _W_ 8 _FMT_  $32 _VALUE_ $128;
  
set &data(obs=10 keep=&vars);
   file FT66F001 col=_col;
   do while(1);
      call vnext(_name_);
      if _name_ eq '_NAME_' then leave;
      _w_     = vformatwx(_name_);
      _value_ = vvaluex(_name_);
      _col_   = _col;
     
put _value_ $varying128. _w_ @;
      if _n_ eq 1 then do;
         _vtype_ = vtypex(_name_);
         _fmt_   = vformatx(_name_);
        
output;
        
end;
     
end;
  
put;
  
format &fmts;
   run;
proc print;
  
run;


12-1-2014 9-02-17 AM.png


12-1-2014 9-04-20 AM.png
zkreflex
Calcite | Level 5

I appreciate all these creative responses, but I really need to write a macro that produces exactly what is shown at the beginning and none of you have addressed the biggest problem I'm having which is getting the column pointer value to work.  Since I posted I've gotten really close, but I'm getting an extra column pointer value after the last variable/format that I don't want...any ideas?

/*Part 9*/

%macro writeoutC (dataset,datafile,variables,formatlist);

%let newput= ;

%let newpointer=1;

%do i=1 %to 4;

%let newpointer=&newpointer+%sysfunc(scan(&formatlist,&i,' $',ap));

%let newput=&newput %sysfunc(scan(&variables,&i,'

')):%sysfunc(scan(&formatlist,&i,' ')) @%eval(&newpointer);

%end;

data _null_;

set &dataset;

file "&datafile";

put @1 &newput; 

run;

%mend writeoutC;

/*Part 10*/

%writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);

Astounding
PROC Star

I think what the other posters have been trying to tell you is this.  You don't need a complicated PUT statement.  This combination would work equally well.  Use the macro variables to generate:

format bookid 4. author $30. isbn isbn18. yearpub comma6.;

Then your PUT statement can be simple:

put bookid author isbn yearpub;

Good luck.

Astounding
PROC Star

Sorry ... I didn't notice the colon snuck into the middle to left-hand justify the written messages.  Here's what you asked for originally:

%do i=1 %to 4;

   %if i=1 %then %let newpointer=1;

   %else %let newpointer = %eval(&newpointer + ... same as before but using &i-1 instead of &i ...);
   %let newput = &newput @&newpointer ... same as before but omit final @%eval ... ;

%end;

Good luck.

data_null__
Jade | Level 19

I did not notice the colon either but then I did not look at the code. :smileysilly: However using the colon changes the PUTting from FORMATTED to LIST which doesn't make sense to me.  Seems like the -L format modified would be a more logical approach unless there is more the OP is not disclosing.

zkreflex
Calcite | Level 5

I tried this:

/*Part 9*/

%macro writeoutC (dataset,datafile,variables,formatlist);

%do i=1 %to 4;

%if &i=1 %then %let newpointer=1;

%else %let newpointer=%eval(&newpointer+&sysfunc(scan(&formatlist,%eval(&i-1),' $',ap)));

%let newput=&newput @&newpointer %sysfunc(scan(&variables,&i,' ')):

%sysfunc(scan(&formatlist,&i,' '));

%end;

data _null_;

set &dataset;

file "&datafile";

put @1 &newput;

run;

%mend writeoutC;

/*Part 10*/

%writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);

And again, I'm trying to exactly produce the following:

data_null_ ;

set books.list;

file "list3.data";

put @1 bookid 4. @5 author $30. @35 isbn isbn18. @43 yearpub comma6.;

run;

But I'm getting the following errors:

WARNING: Apparent symbolic reference NEWPOINTER not resolved.

WARNING: Apparent symbolic reference SYSFUNC not resolved.

WARNING: Apparent symbolic reference NEWPOINTER not resolved.

WARNING: Apparent symbolic reference SYSFUNC not resolved.

ERROR: A character operand was found in the %EVAL function or %IF condition

       where a numeric operand is required. The condition was:

       &newpointer+&sysfunc(scan(4. $30. isbn18. comma6.,0,' $',ap))

ERROR: The macro WRITEOUTC will stop executing.

71       

ballardw
Super User

%if i=1 should be %if &i=1

%sysfunc not &sysfunc  in the %else

Those may be the cause of the %eval error.

zkreflex
Calcite | Level 5

Made the change..now I'm getting the following errors:

WARNING: Apparent symbolic reference NEWPUT not resolved.

ERROR: The text expression &NEWPUT @1 BOOKID:  4. contains a recursive

       reference to the macro variable NEWPUT.  The macro variable will be

       assigned the null value.

WARNING: Apparent symbolic reference SYSFUNC not resolved.

WARNING: Apparent symbolic reference SYSFUNC not resolved.

ERROR: A character operand was found in the %EVAL function or %IF condition

       where a numeric operand is required. The condition was:

       1+&sysfunc(scan(4. $30. isbn18. comma6.,1,' $',ap))

data_null__
Jade | Level 19

OK I fixed your program, so that it produces this confusing mixture of pointer controlled put and list put that would be more easily accomplished almost any other way like adding -L format modifier to each format so that you don't need the : or @col.

@1 bookid:4. @5 author:$30. @35 isbn:isbn18. @53 yearpub:comma6.

%macro writeoutC (dataset,datafile,variables,formatlist);
  
%local newput i newpointer;
  
%do i=1 %to 4;
     
%if &i=1
        
%then %let newpointer=1;
         %else %let newpointer=%eval(&newpointer+%sysfunc(scan(&formatlist,%eval(&i-1),$str( $),ap)));
      %let newput=&newput @&newpointer %sysfunc(scan(&variables,&i,%str( ))):%sysfunc(scan(&formatlist,&i,%str( )));
      %end;

  
%put NOTE: %superq(newput);
  
/*
   data _null_;
   set &dataset;
   file "&datafile";
   put @1 &newput;
   run;
   */

  
%mend writeoutC;

/*Part 10*/

%
writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);


52        
53         %writeoutC (books.list,list2.txt,bookid author isbn yearpub,4. $30. isbn18. comma6.);
NOTE: @1 bookid:4. @5 author:$30. @35 isbn:isbn18. @53 yearpub:comma6.
54        
55        
zkreflex
Calcite | Level 5

Thank you so much, works perfectly!

Oddly enough I think you made a mistake typing:


%eval(&i-1),$str( $),ap)));


I'm guessing it should be:


%eval(&i-1),%str( $),ap)));


but, it works both ways...

data_null__
Jade | Level 19

Mistakes are not odd at all for me. :smileysilly:

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

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
  • 19 replies
  • 1595 views
  • 0 likes
  • 6 in conversation