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

 I have the following code as a test case just trying to get the vlength of the ith macro in my list, but the resolve part of my macro only puts in values for the 4th observation. Even worse, it doesn't run unless I comment out that line for the first pass and then uncomment it. So I obviously have something wrong with the timing of how I think things resolve works in the data step.

 

 

 

options symbolgen;

%macro testmac;
%global classvars;
%let classvars = locale2 key2 lineno2 text2;
%global ndims;
%let ndims=%sysfunc(countw(&classvars,%str( )));
%global iter;
array valength[&ndims];
do i =1 to &ndims;
	call symput('iter', i);
	a = resolve('&iter');
	/* a vars are to show what I expected to get 	 */
	a1= resolve(vlength(%scan(&classvars, 1, %str( ))));
	a2= resolve(vlength(%scan(&classvars, 2, %str( ))));
	a3= resolve(vlength(%scan(&classvars, 3, %str( ))));
	a4= resolve(vlength(%scan(&classvars, 4, %str( ))));
	/* comment out the line below for the initial run then after that it runs */
	valength[i] = resolve(vlength(%scan(&classvars, &iter, %str( ))));
end;
%mend;

data test();
if _n_ = 1;
set sashelp.aacomp();
locale2 = locale;
key2 = key;
lineno2 = lineno;
text2 = text;
%testmac;
run;

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
ballardw
Super User

A couple of points:

If your have a format such as dollar18.2 assigned to a numeric value, which likely has a length of 8, then Vlength will not give you enough spaces for the formatted value if I understand exactly what you are attempting to do.

data example;
   x=3;
   l=vlength(x);
   put l=;
run;

You don't show anything about how you attempt to use the values in that valength array, so I'm not sure that what you are capturing is actually what you need.

The timing of macro functions and data steps is an issue.

I would suggest looking at this code and see if this is getting what you want.

 

%let classvars = locale2 key2 lineno2 text2;
%let ndims=%sysfunc(countw(&classvars,%str( )));

data test();
set sashelp.aacomp(obs=1);
   locale2 = locale;
   key2 = key;
   lineno2 = lineno;
   text2 = text;
   array valength[&ndims];
   do i = 1 to countw("&classvars.");
      valength[i]= vlengthx(scan("&classvars.",i));
   end;

run;

 

 

 

View solution in original post

12 REPLIES 12
Tom
Super User Tom
Super User

Perhaps you are attacking the problem from the wrong direction.  Please create a simple end to end example of what you want to do. Show the input data and the output you want.  Show the SAS code that could use to solve the simple example. Without any macro code or macro variables.  Then you can start to explain what varies between cases, perhaps by showing a second example.

 

In general if you are using a SAS macro it is because you want to conditionally generate code, so you need to know what code will work before you can design the logic of how to create it.  And there could be multiple ways to solve the underlying problem with SAS code and which is easier to generate with macro code might drive the decision as to which to use.

 

weg
Obsidian | Level 7 weg
Obsidian | Level 7

Here is what I want to automate:

 

~

 

 

 

Tom
Super User Tom
Super User

Not really what I was asking.  Take about five steps back up the food chain and explain the real problem.  Not the problem you having with the current approach you are taking to the initial problem.

 

While you think about that here is some things you try to help with your current issue.

The first block of code is easy.  So you start with as input a list of variable names like:

%let varlist=var1 var2 var3 var4 var5;

So just use two %DO loops. Here is the top one.  

%do i=1 %to %sysfunc(countw(&varlist,%str( )));
do d&i=1 to dim(%scan(&varlist,&i,%str( )));
%end;

And then after you generate whatever code goes in the middle of all of those DO loops you need to genrate the END statements for each of them;

%do i=1 %to %sysfunc(countw(&varlist,%str( )));
end;
%end;

Not sure what the rest is supposed to be. Part of it looks like you are trying to find the defined lengths of some variables?  If so you should really do that BEFORE starting the data step where you are going to be putting the generated DO loops.  For example by running PROC CONTENTS on the source data.  How is this macro you are trying to build supposed to know what dataset to check?  For now let's assume you have that in some other macro variable, lets call DSNAME.

proc contents data=&dsname(keep=&varlist) noprint out=contents; run;

Now you can use SAS code to work with that CONTENTS dataset and do whatever math you wanted to do with the LENGTH and/or FORMATL variables.

 

 

weg
Obsidian | Level 7 weg
Obsidian | Level 7

That is the base of my problem.  I am only given the array name and the names of the variables that I need to retrieve values from.  I only know that the array and those variables exist and have values.  everything else I need to figure out in my macro.  I cannot use anything outside of the data step and I cannot make any changes anywhere in the data step, every calculation needs to be completed in the macro.

Tom
Super User Tom
Super User

@weg wrote:

That is the base of my problem.  I am tasked with writing a macro that generates  the print statement I showed you that can be used in any arbitrary data step.  I am only given the array name and the names of the variables that I need to retrieve values from.  I only know that the array and those variables exist and have values.  everything else I need to figure out in my macro.  I cannot use anything outside of the data step and I cannot make any changes anywhere in the data step, every calculation needs to be completed in the macro.


Is this some type of cruel exam?

 

In that case the calculations definitely need to be done in the SAS code the macro generates.   So, again, take a simple example and work it out without ANY macro code.  Once you know what code can work then you can figure out how to generate it using the macro language.

Tom
Super User Tom
Super User

This really is starting to sound like someone is trying to impose a solution on you based on a mental model of how to program that is probably a odds with how SAS works.  

 

It kind of looks like the actual problem is how to produce a report.  Why are you using data step to produce this report instead of just using PROC PRINT? Is there some defined format that the report needs to be in?

weg
Obsidian | Level 7 weg
Obsidian | Level 7

Yes, this is SAS code that was made to work with other code and follow other pipelines that have a different way of processing than sas.  I don't even come from sas background, I'm used to python where we can actually write a function that takes input and produces output, rather than trying to play games shuffling literal text characters around  trying to fall into some hodgepodge of usable code.

ballardw
Super User

A couple of points:

If your have a format such as dollar18.2 assigned to a numeric value, which likely has a length of 8, then Vlength will not give you enough spaces for the formatted value if I understand exactly what you are attempting to do.

data example;
   x=3;
   l=vlength(x);
   put l=;
run;

You don't show anything about how you attempt to use the values in that valength array, so I'm not sure that what you are capturing is actually what you need.

The timing of macro functions and data steps is an issue.

I would suggest looking at this code and see if this is getting what you want.

 

%let classvars = locale2 key2 lineno2 text2;
%let ndims=%sysfunc(countw(&classvars,%str( )));

data test();
set sashelp.aacomp(obs=1);
   locale2 = locale;
   key2 = key;
   lineno2 = lineno;
   text2 = text;
   array valength[&ndims];
   do i = 1 to countw("&classvars.");
      valength[i]= vlengthx(scan("&classvars.",i));
   end;

run;

 

 

 

weg
Obsidian | Level 7 weg
Obsidian | Level 7

Thank you this gives the output I want, thank you and it is way nicer than what I was trying.  Can I ask though what the . does on the macros in this case? there is nothing to delay which is what I though the main point of a . was.

 

That is also a very good point about formatting being longer. I don't thing it would affect my programs too much but I will definitely keep a note of it.

ballardw
Super User

@weg wrote:

Thank you this gives the output I want, thank you and it is way nicer than what I was trying.  Can I ask though what the . does on the macros in this case? there is nothing to delay which is what I though the main point of a . was.

 

That is also a very good point about formatting being longer. I don't thing it would affect my programs too much but I will definitely keep a note of it.


The . is conceptually a macro variable value concatenation. The first place many people encounter this is in a macro value and a file extension such as &filenamevar.txt and expect to have a . before txt in the name find out it isn't there. There are times when you want to combine either multiple macro values or macro values and fixed text. So the . allows that. Suppose you have a macro value that holds the stem (common part) of some variable names such as "student" and are wanting to use the stem to create values like "student_id" "student_grade" or such. If you use &macrovar_id then you get an error because you did not define a macro variable macrovar_id. In this case you would use &macrovar._id &macrovar._grade.

 

For another headache use you can search for "indirect reference macro variables" where you may see things that look like &&var&othervar vs &&var.&othervar which behaves quite differently.

 

While not exactly needed every single time you use a macro variable if you get in the habit of using it you will have fewer problems when you go to writing slightly more complicated macros.

 

 

Tom
Super User Tom
Super User

A couple of things to think about if you are new to SAS.  The defined lengths of those variables are not going to change while the data step is running. So there is no need to recalculate them on every iteration of the data step.  You can use the _N_ automatic macro variable to figure out when your are running the first iteration.  You can either retain the variables used to store the lengths, or use temporary variables that are automatically retained.

   array valength[&ndims] _temporary_;
   if _n_=1 then do i = 1 to &ndims;
      valength[i]= vlengthx(scan("&classvars.",i));
   end;

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 12 replies
  • 1302 views
  • 3 likes
  • 3 in conversation