module Columnize

Adapted from the routine of the same name in cmd.py

Public Class Methods

columnize(list, displaywidth=80, colsep = ' ', arrange_vertical=true, ljust=true, lineprefix='') click to toggle source

or arranged horizontally:

['1', '2,', '3', '4'] => '1  2\n3  4\n'

Each column is only as wide possible, no larger than +displaywidth'. If list is not an array, the empty string, '', is returned. By default, columns are separated by two spaces - one was not legible enough. Set colsep to adjust the string separate columns. If arrange_vertical is set false, consecutive items will go across, left to right, top to bottom.

# File lib/columnize.rb, line 56
def columnize(list, displaywidth=80, colsep = '  ', 
              arrange_vertical=true, ljust=true, lineprefix='')

  # Some degenerate cases
  if not list.is_a?(Array)
    return ''
  end
  if list.size == 0
    return  "<empty>\n"
  end
  l = list.map{|li| li.to_s}
  if 1 == l.size
    return "#{l[0]}\n"
  end

  nrows = ncols = 0  # Make nrows, ncols have more global scope
  colwidths = []     # Same for colwidths
  displaywidth = [4, displaywidth - lineprefix.length].max
  if arrange_vertical
    array_index = lambda {|nrows, row, col| nrows*col + row }
    # Try every row count from 1 upwards
    1.upto(l.size-1) do |_nrows|
      nrows = _nrows
      ncols = (l.size + nrows-1) / nrows
      colwidths = []
      totwidth = -colsep.length

      0.upto(ncols-1) do |_col|
        col = _col
        # get max column width for this column
        colwidth = 0
        0.upto(nrows-1) do |_row|
          row = _row
          i = array_index.call(nrows, row, col)
          if i >= l.size
            break
          end
          colwidth = [colwidth, l[i].size].max
        end
        colwidths << colwidth
        totwidth += colwidth + colsep.length
        if totwidth > displaywidth
          ncols = col
          break
        end
      end
      if totwidth <= displaywidth
        break
      end
    end
    # The smallest number of rows computed and the
    # max widths for each column has been obtained.
    # Now we just have to format each of the
    # rows.
    s = ''
    0.upto(nrows-1) do |_row| 
      row = _row
      texts = []
      0.upto(ncols-1) do |_col|
        col = _col
        i = array_index.call(nrows, row, col)
        if i >= l.size
          x = ""
        else
          x = l[i]
        end
        texts << x
      end
      while texts and texts[-1] == ''
        texts = texts[0..-2]
      end
      if texts.size > 0
        0.upto(texts.size-1) do |_col|
          col = _col
          if ljust
              texts[col] = texts[col].ljust(colwidths[col])
          else
              texts[col] = texts[col].rjust(colwidths[col])
          end
        end
        s += "%s%s\n" % [lineprefix, texts.join(colsep)]
      end
    end
    return s
  else
    array_index = lambda {|nrows, row, col| ncols*(row-1) + col }
    # Try every column count from size downwards
    # Assign to make enlarge scope of loop variables 
    totwidth = i = rounded_size = 0  
    l.size.downto(0) do |_ncols|
      ncols = _ncols
      # Try every row count from 1 upwards
      min_rows = (l.size+ncols-1) / ncols
      min_rows.upto(l.size) do |_nrows|
        nrows = _nrows
        rounded_size = nrows * ncols
        colwidths = []
        totwidth = -colsep.length
        colwidth = row = 0
        0.upto(ncols-1) do |_col|
          col = _col
          # get max column width for this column
          1.upto(nrows) do |_row|
            row = _row
            i = array_index.call(nrows, row, col)
            if i >= rounded_size 
              break
            elsif i < l.size
              colwidth = [colwidth, l[i].size].max
            end
          end
          colwidths << colwidth
          totwidth += colwidth + colsep.length
          if totwidth > displaywidth
            break
          end
        end
        if totwidth <= displaywidth
          # Found the right nrows and ncols
          nrows  = row
          break
        elsif totwidth >= displaywidth
          # Need to reduce ncols
          break
        end
      end
      if totwidth <= displaywidth and i >= rounded_size-1
          break
      end
    end
    # The smallest number of rows computed and the
    # max widths for each column has been obtained.
    # Now we just have to format each of the
    # rows.
    s = ''
    1.upto(nrows) do |row| 
      texts = []
      0.upto(ncols-1) do |col|
        i = array_index.call(nrows, row, col)
        if i >= l.size
          break
        else
          x = l[i]
        end
        texts << x
      end
      0.upto(texts.size-1) do |col|
        if ljust
          texts[col] = texts[col].ljust(colwidths[col])
        else
          texts[col] = texts[col].rjust(colwidths[col])
        end
      end
      s += "%s%s\n" % [lineprefix, texts.join(colsep)]
    end
    return s
  end
end

Private Instance Methods

columnize(list, displaywidth=80, colsep = ' ', arrange_vertical=true, ljust=true, lineprefix='') click to toggle source

or arranged horizontally:

['1', '2,', '3', '4'] => '1  2\n3  4\n'

Each column is only as wide possible, no larger than +displaywidth'. If list is not an array, the empty string, '', is returned. By default, columns are separated by two spaces - one was not legible enough. Set colsep to adjust the string separate columns. If arrange_vertical is set false, consecutive items will go across, left to right, top to bottom.

# File lib/columnize.rb, line 56
def columnize(list, displaywidth=80, colsep = '  ', 
              arrange_vertical=true, ljust=true, lineprefix='')

  # Some degenerate cases
  if not list.is_a?(Array)
    return ''
  end
  if list.size == 0
    return  "<empty>\n"
  end
  l = list.map{|li| li.to_s}
  if 1 == l.size
    return "#{l[0]}\n"
  end

  nrows = ncols = 0  # Make nrows, ncols have more global scope
  colwidths = []     # Same for colwidths
  displaywidth = [4, displaywidth - lineprefix.length].max
  if arrange_vertical
    array_index = lambda {|nrows, row, col| nrows*col + row }
    # Try every row count from 1 upwards
    1.upto(l.size-1) do |_nrows|
      nrows = _nrows
      ncols = (l.size + nrows-1) / nrows
      colwidths = []
      totwidth = -colsep.length

      0.upto(ncols-1) do |_col|
        col = _col
        # get max column width for this column
        colwidth = 0
        0.upto(nrows-1) do |_row|
          row = _row
          i = array_index.call(nrows, row, col)
          if i >= l.size
            break
          end
          colwidth = [colwidth, l[i].size].max
        end
        colwidths << colwidth
        totwidth += colwidth + colsep.length
        if totwidth > displaywidth
          ncols = col
          break
        end
      end
      if totwidth <= displaywidth
        break
      end
    end
    # The smallest number of rows computed and the
    # max widths for each column has been obtained.
    # Now we just have to format each of the
    # rows.
    s = ''
    0.upto(nrows-1) do |_row| 
      row = _row
      texts = []
      0.upto(ncols-1) do |_col|
        col = _col
        i = array_index.call(nrows, row, col)
        if i >= l.size
          x = ""
        else
          x = l[i]
        end
        texts << x
      end
      while texts and texts[-1] == ''
        texts = texts[0..-2]
      end
      if texts.size > 0
        0.upto(texts.size-1) do |_col|
          col = _col
          if ljust
              texts[col] = texts[col].ljust(colwidths[col])
          else
              texts[col] = texts[col].rjust(colwidths[col])
          end
        end
        s += "%s%s\n" % [lineprefix, texts.join(colsep)]
      end
    end
    return s
  else
    array_index = lambda {|nrows, row, col| ncols*(row-1) + col }
    # Try every column count from size downwards
    # Assign to make enlarge scope of loop variables 
    totwidth = i = rounded_size = 0  
    l.size.downto(0) do |_ncols|
      ncols = _ncols
      # Try every row count from 1 upwards
      min_rows = (l.size+ncols-1) / ncols
      min_rows.upto(l.size) do |_nrows|
        nrows = _nrows
        rounded_size = nrows * ncols
        colwidths = []
        totwidth = -colsep.length
        colwidth = row = 0
        0.upto(ncols-1) do |_col|
          col = _col
          # get max column width for this column
          1.upto(nrows) do |_row|
            row = _row
            i = array_index.call(nrows, row, col)
            if i >= rounded_size 
              break
            elsif i < l.size
              colwidth = [colwidth, l[i].size].max
            end
          end
          colwidths << colwidth
          totwidth += colwidth + colsep.length
          if totwidth > displaywidth
            break
          end
        end
        if totwidth <= displaywidth
          # Found the right nrows and ncols
          nrows  = row
          break
        elsif totwidth >= displaywidth
          # Need to reduce ncols
          break
        end
      end
      if totwidth <= displaywidth and i >= rounded_size-1
          break
      end
    end
    # The smallest number of rows computed and the
    # max widths for each column has been obtained.
    # Now we just have to format each of the
    # rows.
    s = ''
    1.upto(nrows) do |row| 
      texts = []
      0.upto(ncols-1) do |col|
        i = array_index.call(nrows, row, col)
        if i >= l.size
          break
        else
          x = l[i]
        end
        texts << x
      end
      0.upto(texts.size-1) do |col|
        if ljust
          texts[col] = texts[col].ljust(colwidths[col])
        else
          texts[col] = texts[col].rjust(colwidths[col])
        end
      end
      s += "%s%s\n" % [lineprefix, texts.join(colsep)]
    end
    return s
  end
end