Module: Columnize

Defined in:
lib/version.rb,
lib/columnize.rb

Overview

Module to format an Array as an Array of String aligned in columns.

:main:README.md

Summary

Display a list of strings as a compact set of columns.

For example, for a line width of 4 characters (arranged vertically):
     ['1', '2,', '3', '4'] => '1  3\n2  4\n'

 or arranged horizontally:
     ['1', '2,', '3', '4'] => '1  2\n3  4\n'

Each column is only as wide as necessary. By default, columns are separated by two spaces. Options are avalable for setting

  • the display width

  • the column separator

  • the line prefix

  • whether to ignore terminal codes in text size calculation

  • whether to left justify text instead of right justify

License

Columnize is copyright © 2007, 2008, 2009, 2010, 2011 Rocky Bernstein <[email protected]>

All rights reserved. You can redistribute and/or modify it under the same terms as Ruby.

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

Constant Summary collapse

VERSION =

The current version of this package

'0.3.4'
DEFAULT_OPTS =

When an option is not specified for the below keys, these are the defaults.

{
  :arrange_array     => false,
  :arrange_vertical  => true,
  :array_prefix      => false,
  :array_suffix      => '',
  :colsep            => '  ',
  :displaywidth      => 80,
  :lineprefix        => '',
  :ljust             => :auto,
  :term_adjust       => false
}

Class Method Summary collapse

Class Method Details

.cell_size(cell, term_adjust) ⇒ Object

Return the length of String cell. If Boolean term_adjust is true, ignore terminal sequences in cell.



91
92
93
94
95
96
97
# File 'lib/columnize.rb', line 91

def cell_size(cell, term_adjust)
  if term_adjust
    cell.gsub(/\e\[.*?m/, '')
  else
    cell
  end.size
end

.columnize(*args) ⇒ Object

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.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/columnize.rb', line 115

def columnize(*args)

  list, opts = parse_columnize_options(args)
  # 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
  opts[:displaywidth] = [4, 
                         opts[:displaywidth] - opts[:lineprefix].length].max
  if opts[:arrange_vertical]
    array_index = lambda {|num_rows, row, col| num_rows*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 = -opts[: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, cell_size(l[i], opts[:term_adjust])].max
        end
        colwidths << colwidth
        totwidth += colwidth + opts[:colsep].length
        if totwidth > opts[:displaywidth]
          ncols = col
          break
        end
      end
      if totwidth <= opts[: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 opts[:ljust]
              texts[col] = texts[col].ljust(colwidths[col])
          else
              texts[col] = texts[col].rjust(colwidths[col])
          end
        end
        s += "%s%s\n" % [opts[:lineprefix], texts.join(opts[:colsep])]
      end
    end
    return s
  else
    array_index = lambda {|num_rows, 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 = -opts[: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, cell_size(l[i], opts[:term_adjust])].max
            end
          end
          colwidths << colwidth
          totwidth += colwidth + opts[:colsep].length
          if totwidth > opts[:displaywidth]
            break
          end
        end
        if totwidth <= opts[:displaywidth]
          # Found the right nrows and ncols
          nrows  = row
          break
        elsif totwidth >= opts[:displaywidth]
          # Need to reduce ncols
          break
        end
      end
      if totwidth <= opts[: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 = ''
    prefix = opts[:array_prefix] || opts[:lineprefix]
    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 opts[:ljust]
          texts[col] = texts[col].ljust(colwidths[col])
        else
          texts[col] = texts[col].rjust(colwidths[col])
        end
      end
      s += "%s%s\n" % [prefix, texts.join(opts[:colsep])]
      prefix = opts[:lineprefix]
    end
    s += opts[:array_suffix]
    return s
  end
end

.parse_columnize_options(args) ⇒ Object

Options parsing routine for Columnize::columnize. In the preferred newer style, args is either a hash where each key is one of the option names:

arrange_vertical

Arrange list vertically rather than horizontally. This is the default

colsep

String used to separate columns

displaywidth

Maximum width of each line

ljust

Boolean or :auto: Left-justify fields in a column? The default is true. If

the :auto, then right-justify if every element of the data is a kind of Numeric.

lineprefix

String: string to prepend to each line. The default is ”.

In the older style positional arguments are used and the positions are in the order: displaywidth, colsep, arrange_vertical, ljust, and lineprefix.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/columnize.rb', line 64

def parse_columnize_options(args)
  list = args.shift
  if 1 == args.size && args[0].kind_of?(Hash)
    opts = DEFAULT_OPTS.merge(args[0])
    if opts[:arrange_array]
      opts[:array_prefix] = '['
      opts[:lineprefix]   = ' '
      opts[:array_suffix] = "]\n"
      opts[:colsep]       = ', '
      opts[:arrange_vertical] = false
    end
    opts[:ljust] = !(list.all?{|datum| datum.kind_of?(Numeric)}) if 
      opts[:ljust] == :auto
    return list, opts
  else      
    opts = DEFAULT_OPTS.dup
    %w(displaywidth colsep arrange_vertical ljust lineprefix
      ).each do |field|
      break if args.empty?
      opts[field.to_sym] = args.shift
    end
    return list, opts
  end
end