Class: TableFu

Inherits:
Object
  • Object
show all
Defined in:
lib/table_fu.rb,
lib/table_fu.rb

Overview

TableFu turns a matric array(from a csv file for example) into a spreadsheet.

Allows formatting, macros, sorting, and faceting.

Documentation: propublica.github.com/table-fu

Defined Under Namespace

Classes: Datum, Formatting, Header, Row

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(table, column_opts = {}) {|_self| ... } ⇒ TableFu

Should be initialized with a matrix array or a string containing a csv, and expects the first array in the matrix to be column headers.

Yields:

  • (_self)

Yield Parameters:

  • _self (TableFu)

    the object that the method was called on



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/table_fu.rb', line 24

def initialize(table, column_opts = {})
  # Assume if we're past a string or filehandle we need to parse a csv
  if table.is_a?(String) || table.is_a?(File)
    table = FasterCSV.parse(table)
  end
  @column_headers = table.slice!(0)
  @totals = {}
  @table = table
  @col_opts = column_opts
  yield self if block_given?
end

Instance Attribute Details

#col_optsObject

Returns the value of attribute col_opts.



20
21
22
# File 'lib/table_fu.rb', line 20

def col_opts
  @col_opts
end

#column_headersObject (readonly)

Returns the value of attribute column_headers.



19
20
21
# File 'lib/table_fu.rb', line 19

def column_headers
  @column_headers
end

#deleted_rowsObject (readonly)

Returns the value of attribute deleted_rows.



19
20
21
# File 'lib/table_fu.rb', line 19

def deleted_rows
  @deleted_rows
end

#faceted_onObject

Returns the value of attribute faceted_on.



20
21
22
# File 'lib/table_fu.rb', line 20

def faceted_on
  @faceted_on
end

#tableObject (readonly)

Returns the value of attribute table.



19
20
21
# File 'lib/table_fu.rb', line 19

def table
  @table
end

#totalsObject (readonly)

Returns the value of attribute totals.



19
20
21
# File 'lib/table_fu.rb', line 19

def totals
  @totals
end

Instance Method Details

#columnsObject

Return the headers defined in column headers or cherry picked from @col_opts



76
77
78
# File 'lib/table_fu.rb', line 76

def columns
  @col_opts[:columns] || column_headers
end

#columns=(array) ⇒ Object

Set up the cherry picked columns



173
174
175
# File 'lib/table_fu.rb', line 173

def columns=(array)
  @col_opts[:columns] = array
end

#delete_rows!(arr) ⇒ Object

Pass it an array and it will delete it from the table, but save the data in @deleted_rows@ for later perusal.

Returns: nothing



43
44
45
46
47
48
49
50
# File 'lib/table_fu.rb', line 43

def delete_rows!(arr)
  @deleted_rows ||= []
  arr.map do |item|
    @deleted_rows << @table[item] #account for header and 0 index
    @table[item] = nil
  end
  @table.compact!
end

#faceted?Boolean

Return true if this table is faceted

Returns:

  • (Boolean)


148
149
150
# File 'lib/table_fu.rb', line 148

def faceted?
  not faceted_on.nil?
end

#faceted_by(column, opts = {}) ⇒ Object

Return an array of TableFu instances grouped by a column.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/table_fu.rb', line 101

def faceted_by(column, opts = {})
  faceted_spreadsheets = {}
  rows.each do |row|
    unless row.column_for(column).value.nil?
      faceted_spreadsheets[row.column_for(column).value] ||= []
      faceted_spreadsheets[row.column_for(column).value] << row
    end
  end

  # Create new table_fu instances for each facet
  tables = []
  faceted_spreadsheets.each do |key,value|
    new_table = [@column_headers] + value
    table = TableFu.new(new_table)
    table.faceted_on = key
    table.col_opts = @col_opts #formatting should be carried through
    tables << table
  end

  tables.sort! do |a,b|
    a.faceted_on <=> b.faceted_on
  end

  if opts[:total]
    opts[:total].each do |c|
      tables.each do |f|
        f.sum_totals_for(c)
      end
    end
  end

  tables
end

#formattingObject

Return the formatting hash



163
164
165
# File 'lib/table_fu.rb', line 163

def formatting
  @col_opts[:formatting]
end

#formatting=(headers) ⇒ Object

Set the formatting hash



168
169
170
# File 'lib/table_fu.rb', line 168

def formatting=(headers)
  @col_opts[:formatting] = headers
end

#headersObject

Return the headers of the array



81
82
83
84
85
86
87
# File 'lib/table_fu.rb', line 81

def headers
  all_columns = []
  columns.each do |header|
    all_columns << TableFu::Header.new(header, header, nil, self)
  end
  all_columns
end

#only!(range) ⇒ Object

Inverse slice: Only keep the rows in the range after sorting



54
55
56
57
58
59
# File 'lib/table_fu.rb', line 54

def only!(range)
  rows_to_exclude = rows.map do |row|
    range.include?(row.row_num) ? nil : row.row_num
  end
  delete_rows!(rows_to_exclude.compact)
end

#row_at(row_num) ⇒ Object

Returns a Row object for the row at a certain index



62
63
64
# File 'lib/table_fu.rb', line 62

def row_at(row_num)
  TableFu::Row.new(@table[row_num], row_num, self)
end

#rowsObject

Returns all the Row objects for this object as a collection



67
68
69
70
71
72
73
# File 'lib/table_fu.rb', line 67

def rows
  all_rows = []
  @table.each_with_index do |row, index|
    all_rows << TableFu::Row.new(row, index, self)
  end
  all_rows.sort
end

#sorted_byObject

Return the sorted_by column



153
154
155
# File 'lib/table_fu.rb', line 153

def sorted_by
  @col_opts[:sorted_by]
end

#sorted_by=(header) ⇒ Object

Set the sorted_by column



158
159
160
# File 'lib/table_fu.rb', line 158

def sorted_by=(header)
  @col_opts[:sorted_by] = header
end

#sum_totals_for(column) ⇒ Object

Sum the values of a particular column



90
91
92
# File 'lib/table_fu.rb', line 90

def sum_totals_for(column)
  @totals[column.to_s] = rows.inject(0) { |sum, r| to_numeric(r.datum_for(column).value) + sum }
end

#to_numeric(num) ⇒ Object

Return a numeric instance for a string number, or if it’s a string we return 1, this way if we total up a series of strings it’s a count



137
138
139
140
141
142
143
144
145
# File 'lib/table_fu.rb', line 137

def to_numeric(num)
  if num.nil?
    0
  elsif num.kind_of? Integer
    num
  else
    1 # We count each instance of a string this way
  end
end

#total_for(column) ⇒ Object

Sum the values of a particular column and return a Datum



95
96
97
98
# File 'lib/table_fu.rb', line 95

def total_for(column)
  sum_totals_for(column)
  Datum.new(@totals[column.to_s], column, nil, self)
end