Align columns: Difference between revisions

From Rosetta Code
Content added Content deleted
(Bug fix, parameter notes, and addition of example.)
(Trailing dollars? Space on RHS is insignificant.)
Line 14: Line 14:
Further,$allow$for$each$word$in$a$column$to$be$either$left$
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.</pre>
justified,$right$justified,$or$center$justified$within$its$column.</pre>

Note that:
# The example input texts lines may, or may not, have trailing dollar characters.
# Consecutive space characters produced adjacent to the end of lines is insignificant for the purposes of the task.

=={{header|Ada}}==
=={{header|Ada}}==
{{libheader|Simple components for Ada}}
{{libheader|Simple components for Ada}}

Revision as of 03:50, 2 December 2008

Task
Align columns
You are encouraged to solve this task according to the task description, using any language you may know.

Given a text file of many lines, where fields within a line are delineated by a single 'dollar' character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.

Use the following text to test your programs:

Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.

Note that:

  1. The example input texts lines may, or may not, have trailing dollar characters.
  2. Consecutive space characters produced adjacent to the end of lines is insignificant for the purposes of the task.

Ada

<ada> with Ada.Characters.Latin_1; use Ada.Characters.Latin_1; with Ada.Text_IO; use Ada.Text_IO; with Strings_Edit; use Strings_Edit;

procedure Column_Aligner is

  Text : constant String :=
     "Given$a$text$file$of$many$lines,$where$fields$within$a$line$" & NUL &
     "are$delineated$by$a$single$'dollar'$character,$write$a$program" & NUL &
     "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" & NUL &
     "column$are$separated$by$at$least$one$space." & NUL &
     "Further,$allow$for$each$word$in$a$column$to$be$either$left$" & NUL &
     "justified,$right$justified,$or$center$justified$within$its$column." & NUL;
  File    : File_Type;
  Width   : array (1..1_000) of Natural := (others => 0);
  Line    : String (1..200);
  Column  : Positive := 1;
  Start   : Positive := 1;
  Pointer : Positive;

begin

  Create (File, Out_File, "columned.txt");
     -- Determining the widths of columns
  for I in Text'Range loop
     case Text (I) is
        when '$' | NUL =>
           Width (Column) := Natural'Max (Width (Column), I - Start + 1);
           Start  := I + 1;
           if Text (I) = NUL then
              Column := 1;
           else
              Column := Column + 1;
           end if;
        when others =>
           null;
     end case;
  end loop;
     -- Formatting
  for Align in Alignment loop
     Column  := 1;
     Start   := 1;
     Pointer := 1;
     for I in Text'Range loop
        case Text (I) is
           when '$' | NUL =>
              Put -- Formatted output of a word
              (  Destination => Line,
                 Pointer     => Pointer,
                 Value       => Text (Start..I - 1),
                 Field       => Width (Column),
                 Justify     => Align
              );
              Start  := I + 1;
              if Text (I) = NUL then
                 Put_Line (File, Line (1..Pointer - 1));
                 Pointer := 1;
                 Column := 1;
              else
                 Column := Column + 1;
              end if;
           when others =>
              null;
        end case;
     end loop;
  end loop;
  Close (File);

end Column_Aligner; </ada> Formatted file sample:

Given      a          text       file   of     many      lines,     where    fields  within  a      line  
are        delineated by         a      single 'dollar'  character, write    a       program 
that       aligns     each       column of     fields    by         ensuring that    words   in     each  
column     are        separated  by     at     least     one        space.   
Further,   allow      for        each   word   in        a          column   to      be      either left  
justified, right      justified, or     center justified within     its      column. 
      Given          a       text   file     of      many     lines,    where  fields  within      a line 
        are delineated         by      a single  'dollar' character,    write       a program
       that     aligns       each column     of    fields         by ensuring    that   words     in each 
     column        are  separated     by     at     least        one   space.
   Further,      allow        for   each   word        in          a   column      to      be either left 
 justified,      right justified,     or center justified     within      its column.
   Given        a        text     file    of      many     lines,     where   fields  within    a   line  
    are    delineated     by        a   single  'dollar' character,   write     a    program 
   that      aligns      each    column   of     fields      by     ensuring   that   words    in   each  
  column       are     separated   by     at     least       one     space.  
 Further,     allow       for     each   word      in         a      column     to      be   either left  
justified,    right   justified,   or   center justified   within      its   column. 

ALGOL 68

STRING nl = REPR 10;
STRING text in list := "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"+nl+
  "are$delineated$by$a$single$'dollar'$character,$write$a$program"+nl+
  "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"+nl+
  "column$are$separated$by$at$least$one$space."+nl+
  "Further,$allow$for$each$word$in$a$column$to$be$either$left$"+nl+
  "justified,$right$justified,$or$center$justified$within$its$column.";

MODE PAGE = FLEX[0,0]STRING;
PAGE page;

PROC flex page = (PAGE in page, INT row, col)PAGE:(
  HEAP FLEX[row, col]STRING out page;
  out page[:1 UPB in page, :2 UPB in page] := in page;
  FOR r TO row DO
    FOR c FROM 2 UPB in page + 1 TO col DO out page[r,c]:="" OD
  OD;
  FOR r FROM 1 UPB in page + 1 TO row DO
    FOR c FROM 1 TO col DO out page[r,c]:="" OD
  OD;
  out page
);

FILE text in file; 
associate(text in file, text in list);
make term(text in file, "$");

on physical file end(text in file, (REF FILE skip)BOOL: stop iteration);
on logical file end(text in file, (REF FILE skip)BOOL: stop iteration);
FOR row DO
  on line end(text in file, (REF FILE skip)BOOL: stop iteration);
  FOR col DO
    STRING tok;
    getf(text in file, ($gx$,tok));
    IF row > 1 UPB page THEN page := flex page(page, row, 2 UPB page) FI; 
    IF col > 2 UPB page THEN page := flex page(page, 1 UPB page, col) FI; 
    page[row,col]:=tok
  OD;
  stop iteration: 
    SKIP
OD;
stop iteration: 
  SKIP;

BEGIN
  PROC aligner = (PAGE in page, PROC (STRING,INT)STRING aligner)VOID:(
    PAGE page := in page;
    [2 UPB page]INT max width;
    FOR col TO 2 UPB page DO
      INT max len:=0; FOR row TO UPB page DO IF UPB page[row,col]>max len THEN max len:=UPB page[row,col] FI OD;
      FOR row TO UPB page DO page[row,col] := aligner(page[row,col], maxlen) OD
    OD;
    printf(($n(UPB page)(n(2 UPB page -1)(gx)gl)$,page))
  );

  PROC left = (STRING in, INT len)STRING: in + " "*(len - UPB in),
       right = (STRING in, INT len)STRING: " "*(len - UPB in) + in,
       centre = (STRING in, INT len)STRING: ( INT pad=len-UPB in;  pad%2*" "+ in + (pad-pad%2)*" " );
  
  []STRUCT(STRING name, PROC(STRING,INT)STRING align) aligners = (("Left",left), ("Left",right), ("Centre",centre));
  
  FOR index TO UPB aligners DO 
    print((new line, "# ",name OF aligners[index]," Column-aligned output:",new line));
    aligner(page, align OF aligners[index]) 
  OD
END

J

In this program the justification is optionally specified by a left argument, and may be specified independently for each column.

colalign=: verb define
 'L' colalign y                   NB. default to left-justify all columns
:
 D=. '$'                          NB. the column delimiter
 afD=. ,&.> (#&D&.> @: (D&~: @: {: &.>))   NB. assure final delimiter (verb)
 T0=. afD <;._2 y                 NB. text arranged as lines
 T1=. > <;._2&.> T0               NB. as word grid
 Alignment=. x, (({:$T1)-#x) # {:x
 if. 'C' e. Alignment do. 
  Colwidths=. >./"2 Sizes=. > #&.>"0 T1
  CPad=. ('C'=Alignment)*"1 <.2%~ Sizes -~"1 Colwidths
  T2=. T1 ,~&.>"0 CPad #&.>"0 ' ' NB. as center-justify-padded grid
  else. T2=. T1 end.
 if. doesR=. 'R' e. Alignment do.
  whereR=.'R'=Alignment
  T3=. (($T2)$whereR)}(,: |.&.>) T2 NB. as right-justify-prepared grid
  else. T3=. T2 end.
 T4=. >@{."1&.> 1 <@|:;.1 |: T3   NB. as columns
 if. doesR do.
  T5=. whereR }(,: |."1&.>) T4    NB. as rt-jstfy-completed columns
  else. T5=. T4 end.
 SP=. <|:,:' '#~#T0  	NB. space-column to interleave with text columns
 >,"1&.>/ }. ,|: SP,,:T5
)

text=: noun define
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.
)

Examples of use:

   colalign text
Given      a          text       file   of     many      lines,     where    fields  within  a      line
are        delineated by         a      single 'dollar'  character, write    a       program            
that       aligns     each       column of     fields    by         ensuring that    words   in     each
column     are        separated  by     at     least     one        space.                              
Further,   allow      for        each   word   in        a          column   to      be      either left
justified, right      justified, or     center justified within     its      column.                    
   
   'C' colalign text
  Given        a         text     file    of     many      lines,    where   fields  within    a    line
   are     delineated     by       a    single 'dollar'  character,  write      a    program            
   that      aligns      each    column   of    fields       by     ensuring  that    words    in   each
  column      are     separated    by     at     least      one      space.                             
 Further,    allow       for      each   word     in         a       column    to      be    either left
justified,   right    justified,   or   center justified   within     its    column.                    
   
   'RRRRCCCL' colalign text
     Given          a       text   file   of     many      lines,   where    fields  within  a      line
       are delineated         by      a single 'dollar'  character, write    a       program            
      that     aligns       each column   of    fields       by     ensuring that    words   in     each
    column        are  separated     by   at     least      one     space.                              
  Further,      allow        for   each  word     in         a      column   to      be      either left
justified,      right justified,     or center justified   within   its      column.                    

OCaml

<ocaml>#load "str.cma" open Str

let input = "\ Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column."

let () =

 let lines = split (regexp_string "\n") input in
 let fields_l = List.map (split (regexp_string "$")) lines in
 let fields_l = List.map Array.of_list fields_l in
 let n = (* number of columns *)
   List.fold_left
     (fun n fields -> max n (Array.length fields))
     0 fields_l
 in
 let pads = Array.make n 0 in
 List.iter (
   (* calculate the max padding for each column *)
   Array.iteri
     (fun i word -> pads.(i) <- max pads.(i) (String.length word))
 ) fields_l;
 let print f =
   List.iter (fun fields ->
     Array.iteri (fun i word ->
       f word (pads.(i) - (String.length word))
     ) fields;
     print_newline()
   ) fields_l;
 in
 (* left column-aligned output *)
 print (fun word pad ->
   let spaces = String.make pad ' ' in
   Printf.printf "%s%s " word spaces);
 (* right column-aligned output *)
 print (fun word pad ->
   let spaces = String.make pad ' ' in
   Printf.printf "%s%s " spaces word);
 (* center column-aligned output *)
 print (fun word pad ->
   let pad1 = pad / 2 in
   let pad2 = pad - pad1 in
   let sp1 = String.make pad1 ' ' in
   let sp2 = String.make pad2 ' ' in
   Printf.printf "%s%s%s " sp1 word sp2);
</ocaml>

Python

<python>from StringIO import StringIO

textinfile = Given$a$text$file$of$many$lines,$where$fields$within$a$line$ are$delineated$by$a$single$'dollar'$character,$write$a$program that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$ column$are$separated$by$at$least$one$space. Further,$allow$for$each$word$in$a$column$to$be$either$left$ justified,$right$justified,$or$center$justified$within$its$column.

j2justifier = dict(L=str.ljust, R=str.rjust, C=str.center)

def aligner(infile, justification = 'L'):

  \
 Justify columns of textual tabular input where the record separator is the newline
 and the field separator is a 'dollar' character.
 justification can be L, R, or C; (Left, Right, or Centered).
 
 Return the justified output as a string
 
 assert justification in 'LRC', "justification can be L, R, or C; (Left, Right, or Centered)."
 justifier = j2justifier[justification]
 
 fieldsbyrecord= [line.strip().split('$') for line in infile]
 # pad to same number of fields per record
 maxfields = max(len(record) for record in fieldsbyrecord)
 fieldsbyrecord = [fields + []*(maxfields - len(fields))
                   for fields in fieldsbyrecord]
 # rotate
 fieldsbycolumn = zip(*fieldsbyrecord)
 # calculate max fieldwidth per column
 colwidths = [max(len(field) for field in column)
              for column in fieldsbycolumn]
 # pad fields in columns to colwidth with spaces
 fieldsbycolumn = [ [justifier(field, width) for field in column]
                    for width, column in zip(colwidths, fieldsbycolumn) ]
 # rotate again
 fieldsbyrecord = zip(*fieldsbycolumn)
 return "\n".join( " ".join(record) for record in fieldsbyrecord)


for align in 'Left Right Center'.split():

 infile = StringIO(textinfile)
 print "\n# %s Column-aligned output:" % align
 print aligner(infile, align[0])</python>

Example output:

# Left Column-aligned output:
Given      a          text       file   of     many      lines,     where    fields  within  a      line 
are        delineated by         a      single 'dollar'  character, write    a       program             
that       aligns     each       column of     fields    by         ensuring that    words   in     each 
column     are        separated  by     at     least     one        space.                               
Further,   allow      for        each   word   in        a          column   to      be      either left 
justified, right      justified, or     center justified within     its      column.                     

# Right Column-aligned output:
     Given          a       text   file     of      many     lines,    where  fields  within      a line 
       are delineated         by      a single  'dollar' character,    write       a program             
      that     aligns       each column     of    fields         by ensuring    that   words     in each 
    column        are  separated     by     at     least        one   space.                             
  Further,      allow        for   each   word        in          a   column      to      be either left 
justified,      right justified,     or center justified     within      its column.                     

# Center Column-aligned output:
  Given        a         text     file    of      many     lines,    where    fields  within   a    line 
   are     delineated     by       a    single  'dollar' character,  write      a    program             
   that      aligns      each    column   of     fields      by     ensuring   that   words    in   each 
  column      are     separated    by     at     least      one      space.                              
 Further,    allow       for      each   word      in        a       column     to      be   either left 
justified,   right    justified,   or   center justified   within     its    column.