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