DATA Step, Macro, Functions and more

Macro help with embedded do loop

Accepted Solution Solved
Reply
Occasional Contributor
Posts: 7
Accepted Solution

Macro help with embedded do loop

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...


Accepted Solutions
Solution
‎12-02-2014 08:59 AM
Respected Advisor
Posts: 3,777

Re: Macro help with embedded do loop

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


All Replies
Super User
Posts: 6,939

Re: Macro help with embedded do loop

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.

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Super User
Super User
Posts: 7,401

Re: Macro help with embedded do loop

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;

Respected Advisor
Posts: 3,777

Re: Macro help with embedded do loop

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.

Respected Advisor
Posts: 3,777

Re: Macro help with embedded do loop

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
Occasional Contributor
Posts: 7

Re: Macro help with embedded do loop

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.);

Super User
Posts: 5,083

Re: Macro help with embedded do loop

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.

Super User
Posts: 5,083

Re: Macro help with embedded do loop

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.

Respected Advisor
Posts: 3,777

Re: Macro help with embedded do loop

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.

Occasional Contributor
Posts: 7

Re: Macro help with embedded do loop

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       

Super User
Posts: 10,500

Re: Macro help with embedded do loop

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

%sysfunc not &sysfunc  in the %else

Those may be the cause of the %eval error.

Occasional Contributor
Posts: 7

Re: Macro help with embedded do loop

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))

Solution
‎12-02-2014 08:59 AM
Respected Advisor
Posts: 3,777

Re: Macro help with embedded do loop

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        
Occasional Contributor
Posts: 7

Re: Macro help with embedded do loop

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...

Respected Advisor
Posts: 3,777

Re: Macro help with embedded do loop

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

☑ This topic is SOLVED.

Need further help from the community? Please ask a new question.

Discussion stats
  • 19 replies
  • 591 views
  • 0 likes
  • 6 in conversation