SAS Programming

DATA Step, Macro, Functions and more
BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Niugg2010
Obsidian | Level 7

Hello,

I want to get listing output_1(shown below, the subcat lines should be indented). However when I ran my below code I got output_2 (shown below). I tried to use 'style' to control the output, and 'style' worked for rtf output, but not for listing output. Can someone help me out about how to control indent for listing output? Thanks

 

output_1                                                                                output_2                                                            

fig_1.JPG     fig_2.JPG

 

data x;
	format x $100.;
	x='cat_A';
	output;
	x=repeat("Subcat ", 30);
	output;
run;
ods listing file="xxx\try_1.lst";
proc report data=x;
	column  x;
	define x / 'HEADER' width=50 flow;
run;
ods listing close;

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

Like I said.  Write your own wrapping logic.

Here is such a program from years ago.  You would need to post-process the output of this to prepend the leading spaces for the indentation.

%macro wrap
/*------------------------------------------------------------------------------
Wrap long string into mulitple observations
------------------------------------------------------------------------------*/
(dsnin=    /* Input dataset */
,dsnout=   /* Output dataset */
,invar=    /* Variable to wrap */
,outvar=   /* Name of wrapped variable */
,len=80    /* Length to wrap */
,split='0d'x /* Character(s) to use for forcing new observation */
,break=",- " /* Character(s) used to break long words across lines */
);
/*------------------------------------------------------------------------------
Wrap a long variable into multiple lines. Split characters will cause
the starting of a new line.  Break characters will control where words can be
broken to fit into a line.
------------------------------------------------------------------------------*/
%local macro parmerr varlen vartype;
%let macro=WRAP;

%parmv(dsnin,_req=1)
%parmv(dsnout,_req=1)
%parmv(invar,_req=1)
%parmv(outvar,_req=1)
%parmv(len,_val=positive,_req=1)
%let varlen=%varexist(&dsnin,&outvar,len);
%let vartype=%varexist(&dsnin,&outvar,type);
%if (&vartype = C) %then %do;
  %if (&varlen and (&varlen < &len) ) %then
    %parmv(len,_msg=&outvar already is defined in &dsnin as length &varlen)
  ;
%end;
%else %do;
  %parmv(outvar,_msg=&outvar is defined in &dsnin as numeric)
  %let vartype=%str(numeric );
%end;

%if (&parmerr) %then %goto quit;

*------------------------------------------------------------------------------;
* Wrap variable into multiple observations ;
*------------------------------------------------------------------------------;
data &dsnout;
  &outvar = repeat(' ',&len-1);
  set &dsnin;
  drop _save _pnum _done _split _para _lnum;
  _save=&invar;
  _pnum=0;
  _done=0;
  do until (_done);
    _pnum+1;
%if %length(&split) %then %do;
    _split = indexc(_save,&split);
%end;
%else %do;
    _split=0;
%end;
    if (_split > 1) then do;
      _para = substr(_save,1,_split-1);
      _save = substr(_save,_split+1);
    end;
    else if (_split = 1) then do;
      _para = ' ';
      _save = substr(_save,2);
    end;
    else do;
      _para = _save;
      _done=1;
    end;
    _lnum=1;
    do while (length(_para) > &len);
      &outvar = substr(_para,1,&len);
      _split =indexc(&outvar,&break);
      * ? Need to move a broken word from current line to next line ? ;
      if length(compress(substr(_para,&len,2))) = 2 and (_split > 0) then do;
        _split = indexc( reverse(trim(&outvar)) ,&break);
        &outvar = substr(&outvar,1,&len - _split + 1);
      end;
      output;
      _lnum+1;
      _para = left(substr(_para,length(&outvar)+1));
    end;
    if (_lnum=1) or (_para ne ' ') then do;
      &outvar = _para;
      output;
    end;
  end;

run;

%quit:
%mend wrap;

View solution in original post

12 REPLIES 12
ballardw
Super User

That really looks like you want the long text to be a different variable. ANY property applied to the long text will be the same for the Cat_A unless you do something by parsing the values in a COMPUTE block, and I'm not sure what that compute block would look like.

 

 

Niugg2010
Obsidian | Level 7
Thanks. I also tried compute block and used call define to set style. This did not work on listing output.
Tom
Super User Tom
Super User

To indent the value just add spaces in the front of the value.

If you want the flowed lines to also indent then you probably need to wrap the text yourself.

Example :

data x;
	length x $100;
	x='cat_A';
	output;
  x=substr('  ' || repeat('lorum ipsum ',4),1,50);
	output;
  x=substr('  ' || repeat('lorum ipsum ',4),1,50);
	output;
run;

Result:

  HEADER
  cat_A
    lorum ipsum lorum ipsum lorum ipsum lorum ipsum
    lorum ipsum lorum ipsum lorum ipsum lorum ipsum

PS Why did you attach the $100. format specification to the variable X?  If you want to define a variable use the LENGTH statement or the LENGTH= attribute of the ATTRIB statement.

Niugg2010
Obsidian | Level 7

Thanks for your suggestion. Adding spaces is what I used in my report. 

However, if the 'subcat ' text is very long and show in multiple lines, only the first line is indented, which makes the report difficult to read. In your output, if you set the width is small (eg. width=30 in define) in your proc report, you will find the issue.

 

 

 

Tom
Super User Tom
Super User

Like I said.  Write your own wrapping logic.

Here is such a program from years ago.  You would need to post-process the output of this to prepend the leading spaces for the indentation.

%macro wrap
/*------------------------------------------------------------------------------
Wrap long string into mulitple observations
------------------------------------------------------------------------------*/
(dsnin=    /* Input dataset */
,dsnout=   /* Output dataset */
,invar=    /* Variable to wrap */
,outvar=   /* Name of wrapped variable */
,len=80    /* Length to wrap */
,split='0d'x /* Character(s) to use for forcing new observation */
,break=",- " /* Character(s) used to break long words across lines */
);
/*------------------------------------------------------------------------------
Wrap a long variable into multiple lines. Split characters will cause
the starting of a new line.  Break characters will control where words can be
broken to fit into a line.
------------------------------------------------------------------------------*/
%local macro parmerr varlen vartype;
%let macro=WRAP;

%parmv(dsnin,_req=1)
%parmv(dsnout,_req=1)
%parmv(invar,_req=1)
%parmv(outvar,_req=1)
%parmv(len,_val=positive,_req=1)
%let varlen=%varexist(&dsnin,&outvar,len);
%let vartype=%varexist(&dsnin,&outvar,type);
%if (&vartype = C) %then %do;
  %if (&varlen and (&varlen < &len) ) %then
    %parmv(len,_msg=&outvar already is defined in &dsnin as length &varlen)
  ;
%end;
%else %do;
  %parmv(outvar,_msg=&outvar is defined in &dsnin as numeric)
  %let vartype=%str(numeric );
%end;

%if (&parmerr) %then %goto quit;

*------------------------------------------------------------------------------;
* Wrap variable into multiple observations ;
*------------------------------------------------------------------------------;
data &dsnout;
  &outvar = repeat(' ',&len-1);
  set &dsnin;
  drop _save _pnum _done _split _para _lnum;
  _save=&invar;
  _pnum=0;
  _done=0;
  do until (_done);
    _pnum+1;
%if %length(&split) %then %do;
    _split = indexc(_save,&split);
%end;
%else %do;
    _split=0;
%end;
    if (_split > 1) then do;
      _para = substr(_save,1,_split-1);
      _save = substr(_save,_split+1);
    end;
    else if (_split = 1) then do;
      _para = ' ';
      _save = substr(_save,2);
    end;
    else do;
      _para = _save;
      _done=1;
    end;
    _lnum=1;
    do while (length(_para) > &len);
      &outvar = substr(_para,1,&len);
      _split =indexc(&outvar,&break);
      * ? Need to move a broken word from current line to next line ? ;
      if length(compress(substr(_para,&len,2))) = 2 and (_split > 0) then do;
        _split = indexc( reverse(trim(&outvar)) ,&break);
        &outvar = substr(&outvar,1,&len - _split + 1);
      end;
      output;
      _lnum+1;
      _para = left(substr(_para,length(&outvar)+1));
    end;
    if (_lnum=1) or (_para ne ' ') then do;
      &outvar = _para;
      output;
    end;
  end;

run;

%quit:
%mend wrap;
Niugg2010
Obsidian | Level 7
This macro is very interesting. I guess it should work for my case.
Niugg2010
Obsidian | Level 7
I an trying to use your macro, and it has errors. I guess I missed %parmv and %varexist macros.
Tom
Super User Tom
Super User

@Niugg2010 wrote:
I an trying to use your macro, and it has errors. I guess I missed %parmv and %varexist macros.

You should be able to comment out the parameter validation section of the macro.  Or you could get those macros from GITHUB.

 

Here is an example:

data test1;
 length invar $ 4000 outvar $40;
 input invar $80.;
*---+----10---+----20---+----30---+----40---+----50 ;
cards;
This is a line that is long, and should wrap
This is two paragraphs./This is a line that is long-And should wrap
This is two splits in a row.//How does it work?
This hyphen is right at the b-oundary.
Try it when a word ends right. On the boundry.
Check if a word_is_longer_than_a_single_line. What will it do?
/First character is split.
Last character is split./

run;
options mprint;
%wrap
(dsnin=test1
,dsnout=test2
,invar=invar
,outvar=outvar
,len=30
,split="/"
);

proc print;
 var outvar invar;
run;
Niugg2010
Obsidian | Level 7
Tom, Thank you very much.
Niugg2010
Obsidian | Level 7
Can you give me some examples to use this macro?
Tom
Super User Tom
Super User

To get PROC REPORT to insert group heading lines use COMPUTE blocks.

data x;
  length group $50 x $100;
  group='cat_A';
  x = repeat('lorum ipsum ',20);
  space='   ';
run;
proc report data=x;
  column  space group x;
  define space / width=2 spacing=0 ' ' ;
  define group / group noprint;
  compute before group;
     line @1 group $50. ;
  endcomp;
  define x / 'HEADER'  width=50 flow;
run;
    HEADER
cat_A
    lorum ipsum lorum ipsum lorum ipsum lorum ipsum
    lorum ipsum lorum ipsum lorum ipsum lorum ipsum
    loru

Niugg2010
Obsidian | Level 7

Thanks a lot.

That is interesting. I should gave a more closer example.

Please see below example. This example only have 2 groups (group 1 and 2), but actually we have more groups, so the "System Organ Class ...." column can not be wider.  The Yellow marked part is SOC, and gray marked part is PT. The blue circled items have two rows due to the column width is not enough, and I want the two rows indented, not only the first row, so that the output should look better. If output is rft, it is easy to do. But if output is listing, I have not figured out a good method.

 

 

f1.JPG

sas-innovate-white.png

Our biggest data and AI event of the year.

Don’t miss the livestream kicking off May 7. It’s free. It’s easy. And it’s the best seat in the house.

Join us virtually with our complimentary SAS Innovate Digital Pass. Watch live or on-demand in multiple languages, with translations available to help you get the most out of every session.

 

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.

SAS Training: Just a Click Away

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

Browse our catalog!

Discussion stats
  • 12 replies
  • 4078 views
  • 0 likes
  • 3 in conversation