class SpreadBase::Table

Represents the abstraction of a table and its contents.

The max width of the table is 1024 cells - the last one being 'AMJ'.

Row indexing follows the ruby semantics:

Attributes

name[RW]

Public Class Methods

new(name, raw_data=[]) click to toggle source

params:

name

(required) Name of the table

raw_data

(Array.new) 2d matrix of the data. if not empty, the rows need to be all of the same size

# File lib/spreadbase/table.rb, line 26
def initialize(name, raw_data=[])
  raise "Table name required" if name.nil? || name == ''

  @name                = name
  self.data            = raw_data
  @column_width_styles = []
end

Public Instance Methods

[](column_identifier, row_index, options={}) click to toggle source

Access a cell value.

params:

column_indentifier

either an int (0-based) or the excel-format identifier (AA…); limited to the given row size.

row_index

int (0-based). see notes about the rows indexing.

returns the value, which is automatically converted to the Ruby data type.

# File lib/spreadbase/table.rb, line 51
def [](column_identifier, row_index, options={})
  the_row = row(row_index, options)

  column_index = decode_column_identifier(column_identifier)

  check_column_index(the_row, column_index)

  the_row[column_index]
end
[]=(column_identifier, row_index, value) click to toggle source

Writes a value in a cell.

params:

column_indentifier

either an int (0-based) or the excel-format identifier (AA…); limited to the given row size.

row_index

int (0-based). see notes about the rows indexing.

value

value

# File lib/spreadbase/table.rb, line 69
def []=(column_identifier, row_index, value)
  check_row_index(row_index)

  the_row      = @data[row_index]
  column_index = decode_column_identifier(column_identifier)

  check_column_index(the_row, column_index)

  the_row[column_index] = value_to_cell(value)
end
append_column(column) click to toggle source
# File lib/spreadbase/table.rb, line 243
def append_column(column)
  column_index = @data.size > 0 ? @data.first.size : 0

  insert_column(column_index, column)
end
append_row(row) click to toggle source

This operation won't modify the column width styles in any case.

# File lib/spreadbase/table.rb, line 137
def append_row(row)
  insert_row(@data.size, row)
end
column(column_identifier, options={}) click to toggle source

Returns an array containing the values of a single column.

WATCH OUT! This method doesn't have the range restrictions that axis indexes generally has, that is, it's possible to access a column outside the boundaries of the rows - it will return nil for each of those values.

params:

column_indentifier

for single access, us either an int (0-based) or the excel-format identifier (AA…). when int, follow the same idea of the rows indexing (ruby semantics). for multiple access, use a range either of int or excel-format identifiers - pay attention, because ( 'A'..'c' ) is not semantically correct. interestingly, ruby letter ranges convention is the same as the excel columns one.

# File lib/spreadbase/table.rb, line 152
def column(column_identifier, options={})
  if column_identifier.is_a?(Range)
    min_index = decode_column_identifier(column_identifier.min)
    max_index = decode_column_identifier(column_identifier.max)

    (min_index..max_index).map do | column_index |
      @data.map do | the_row |
        cell = the_row[column_index]

        cell_to_value(cell, options)
      end
    end
  else
    column_index = decode_column_identifier(column_identifier)

    @data.map do | the_row |
      cell = the_row[column_index]

      cell_to_value(cell, options)
    end
  end
end
data(options={}) click to toggle source
# File lib/spreadbase/table.rb, line 38
def data(options={})
  @data.map { | the_row | the_row.map { | cell | cell_to_value(cell, options) } }
end
data=(the_data) click to toggle source
# File lib/spreadbase/table.rb, line 34
def data=(the_data)
  @data = the_data.map { | the_row | array_to_cells(the_row) }
end
delete_column(column_identifier) click to toggle source

Deletes a column.

See Table#column for the indexing notes.

params:

column_indentifier

See Table#column

returns the deleted column

# File lib/spreadbase/table.rb, line 185
def delete_column(column_identifier)
  if column_identifier.is_a?(Range)
    min_index = decode_column_identifier(column_identifier.min)
    max_index = decode_column_identifier(column_identifier.max)

    reverse_result = max_index.downto(min_index).map do | column_index |
      @data.map do | row |
        cell = row.slice!(column_index)

        cell_to_value(cell)
      end
    end

    reverse_result.reverse
  else
    column_index = decode_column_identifier(column_identifier)

    @column_width_styles.slice!(column_index)

    @data.map do | row |
      cell = row.slice!(column_index)

      cell_to_value(cell)
    end
  end
end
delete_row(row_index) click to toggle source

Deletes a row.

This operation won't modify the column width styles in any case.

params:

row_index

int or range (0-based). see notes about the rows indexing.

returns the deleted row

# File lib/spreadbase/table.rb, line 106
def delete_row(row_index)
  check_row_index(row_index)

  deleted_cells = @data.slice!(row_index)

  if row_index.is_a?(Range)
    deleted_cells.map { | row | cells_to_array(row) }
  else
    cells_to_array(deleted_cells)
  end
end
insert_column(column_identifier, column) click to toggle source

Inserts a column.

WATCH OUT! This method doesn't have the range restrictions that axis indexes generally has, that is, it's possible to insert a column outside the boundaries of the rows - it will fill the cells in the middle with nils..

params:

column_indentifier

either an int (0-based) or the excel-format identifier (AA…). when int, follow the same idea of the rows indexing (ruby semantics).

column

array of values. if the table is not empty, it must have the same size of the table height.

# File lib/spreadbase/table.rb, line 222
def insert_column(column_identifier, column)
  raise "Inserting column size (#{ column.size }) different than existing columns size (#{ @data.size })" if @data.size > 0 && column.size != @data.size

  column_index = decode_column_identifier(column_identifier)

  @column_width_styles.insert(column_index, nil)

  if @data.size > 0
    @data.zip(column).each do | row, value |
      cell = value_to_cell(value)

      row.insert(column_index, cell)
    end
  else
    @data = column.map do | value |
      [value_to_cell(value)]
    end
  end

end
insert_row(row_index, row) click to toggle source

Inserts a row.

This operation won't modify the column width styles in any case.

params:

row_index

int (0-based). must be between 0 and (including) the table rows size.

row

array of values. if the table is not empty, must have the same size of the table width.

# File lib/spreadbase/table.rb, line 127
def insert_row(row_index, row)
  check_row_index(row_index, allow_append: true)

  cells = array_to_cells(row)

  @data.insert(row_index, cells)
end
row(row_index, options={}) click to toggle source

Returns an array containing the values of a single row.

params:

row_index

int or range (0-based). see notes about the rows indexing.

# File lib/spreadbase/table.rb, line 86
def row(row_index, options={})
  check_row_index(row_index)

  if row_index.is_a?(Range)
    @data[row_index].map { | row | cells_to_array(row, options) }
  else
    cells_to_array(@data[row_index], options)
  end
end
to_s(options={}) click to toggle source

returns a matrix representation of the tables, with the values being separated by commas.

# File lib/spreadbase/table.rb, line 251
def to_s(options={})
  pretty_print_rows(data, options)
end

Private Instance Methods

array_to_cells(the_row) click to toggle source
# File lib/spreadbase/table.rb, line 257
def array_to_cells(the_row)
  the_row.map { | value | value_to_cell(value) }
end
cell_to_value(cell, options={}) click to toggle source
# File lib/spreadbase/table.rb, line 269
def cell_to_value(cell, options={})
  as_cell = options[:as_cell]

  if as_cell
    cell
  else
    cell.value if cell
  end
end
cells_to_array(cells, options={}) click to toggle source
# File lib/spreadbase/table.rb, line 265
def cells_to_array(cells, options={})
  cells.map { | cell | cell_to_value(cell, options) }
end
check_column_index(row, column_index) click to toggle source
# File lib/spreadbase/table.rb, line 296
def check_column_index(row, column_index)
  raise "Invalid column index (#{ column_index }) for the given row - allowed 0 to #{ row.size - 1 }" if column_index >= row.size
end
check_row_index(row_index, options={}) click to toggle source

Check that row index points to an existing record, or, in case of :allow_append, point to one unit above the last row.

options:

allow_append

Allow pointing to one unit above the last row.

# File lib/spreadbase/table.rb, line 286
def check_row_index(row_index, options={})
  allow_append = options [:allow_append]

  positive_limit = allow_append ? @data.size : @data.size - 1

  row_index = row_index.max if row_index.is_a?(Range)

  raise "Invalid row index (#{ row_index }) - allowed 0 to #{ positive_limit }" if row_index < 0 || row_index > positive_limit
end
decode_column_identifier(column_identifier) click to toggle source

Accepts either an integer, or a MoFoBase26BisexNumber.

Raises an error for invalid identifiers/indexes.

returns a 0-based decimal number.

# File lib/spreadbase/table.rb, line 306
def decode_column_identifier(column_identifier)
  if column_identifier.is_a?(Integer)
    raise "Negative column indexes not allowed: #{ column_identifier }" if column_identifier < 0

    column_identifier
  else
    letters       = column_identifier.upcase.chars.to_a
    upcase_a_ord  = 65

    raise "Invalid letter for in column identifier (allowed 'a/A' to 'z/Z')" if letters.any? { | letter | letter < 'A' || letter > 'Z' }

    base_10_value = letters.inject(0) do | sum, letter |
      letter_ord = letter.unpack('C').first
      sum * 26 + (letter_ord - upcase_a_ord + 1)
    end

    base_10_value -= 1

    # -1 is an empty string
    #
    raise "Invalid literal column identifier (allowed 'A' to 'AMJ')" if base_10_value < 0 || 1023 < base_10_value

    base_10_value
  end
end
value_to_cell(value) click to toggle source
# File lib/spreadbase/table.rb, line 261
def value_to_cell(value)
  value.is_a?(Cell) ? value : Cell.new(value)
end