lib/fat_table/table.rb in fat_table-0.4.0 vs lib/fat_table/table.rb in fat_table-0.4.2
- old
+ new
@@ -67,10 +67,27 @@
@boundaries = []
end
# :category: Constructors
+ # Return an empty duplicate of self. This allows the library to create an
+ # empty table that preserves all the instance variables from self. Even
+ # though FatTable::Table objects have no instance variables, a class that
+ # inherits from it might.
+ def empty_dup
+ self.dup.__empty!
+ end
+
+ def __empty!
+ @columns = []
+ @boundaries = []
+ self
+ end
+
+
+ # :category: Constructors
+
# Construct a Table from the contents of a CSV file named +fname+. Headers
# will be taken from the first CSV row and converted to symbols.
def self.from_csv_file(fname)
File.open(fname, 'r') do |io|
from_csv_io(io)
@@ -595,12 +612,16 @@
key1 = sort_heads.map { |h| rev_heads.include?(h) ? r2[h] : r1[h] }
key2 = sort_heads.map { |h| rev_heads.include?(h) ? r1[h] : r2[h] }
key1 <=> key2
end
# Add the new rows to the table, but mark a group boundary at the points
- # where the sort key changes value.
- new_tab = Table.new
+ # where the sort key changes value. NB: I use self.class.new here
+ # rather than Table.new because if this class is inherited, I want the
+ # new_tab to be an instance of the subclass. With Table.new, this
+ # method's result will be an instance of FatTable::Table rather than of
+ # the subclass.
+ new_tab = empty_dup
last_key = nil
new_rows.each_with_index do |nrow, k|
new_tab << nrow
key = nrow.fetch_values(*sort_heads)
new_tab.mark_boundary(k - 1) if last_key && key != last_key
@@ -711,11 +732,11 @@
end
ev = Evaluator.new(ivars: ivars,
before: before_hook,
after: after_hook)
# Compute the new Table from this Table
- result = Table.new
+ result = empty_dup
normalize_boundaries
rows.each_with_index do |old_row, old_k|
# Set the group number in the before hook and run the hook with the
# local variables set to the row before the new row is evaluated.
grp = row_index_to_group_index(old_k)
@@ -768,11 +789,11 @@
#
# tab.where('date > Date.today - 30') => rows with recent dates
# tab.where('@row.even? && shares > 500') => even rows with lots of shares
def where(expr)
expr = expr.to_s
- result = Table.new
+ result = empty_dup
headers.each do |h|
col = Column.new(header: h)
result.add_column(col)
end
ev = Evaluator.new(ivars: { row: 0, group: 0 })
@@ -790,11 +811,11 @@
# :category: Operators
# Return a new table with all duplicate rows eliminated. Resets groups. Same
# as #uniq.
def distinct
- result = Table.new
+ result = empty_dup
uniq_rows = rows.uniq
uniq_rows.each do |row|
result << row
end
result
@@ -902,11 +923,11 @@
unless columns.map(&:type) == other.columns.map(&:type)
msg = "can't apply a set ops to tables with different column types."
raise UserError, msg
end
other_rows = other.rows.map { |r| r.replace_keys(headers) }
- result = Table.new
+ result = empty_dup
new_rows = rows.send(oper, other_rows)
new_rows.each_with_index do |row, k|
result << row
result.mark_boundary if k == size - 1 && add_boundaries
end
@@ -1009,11 +1030,11 @@
self_row_nils = headers.map { |h| [h, nil] }.to_h
other_row_nils = other.headers.map { |h| [h, nil] }.to_h
join_exp, other_common_heads =
build_join_expression(exps, other, join_type)
ev = Evaluator.new
- result = Table.new
+ result = empty_dup
other_rows = other.rows
other_row_matches = Array.new(other_rows.size, false)
rows.each do |self_row|
self_row_matched = false
other_rows.each_with_index do |other_row, k|
@@ -1257,10 +1278,10 @@
def group_by(*group_cols, **agg_cols)
sorted_tab = order_by(group_cols)
groups = sorted_tab.rows.group_by do |r|
group_cols.map { |k| r[k] }
end
- result = Table.new
+ result = empty_dup
groups.each_pair do |_vals, grp_rows|
result << row_from_group(grp_rows, group_cols, agg_cols)
end
result.normalize_boundaries
result