table_transform 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 91c96009ab39b8d65825644044ebd64e75b85994
4
- data.tar.gz: 7f6c16447953380214f627ce735bbe90bedcd1dd
3
+ metadata.gz: 9eeed52b5a7aa9db9eea7f0e0f27a40ff2b932be
4
+ data.tar.gz: bad8620c79b09929f105d54894b4df931a8b778b
5
5
  SHA512:
6
- metadata.gz: 3cf48bcd2ed716bc0c0c2c00d43f8d6d2bd6165f790c75bfb20e496a2aa7afe223012f1a91495d0d77c36973c42c9f6ccfcb5620efc5e7f79a72ae57a1427bf3
7
- data.tar.gz: f3e61219d7987ef67a8070cd6e11228c0ab138a7475d2890e20a12e2c03737bf0fca5522b44804ab21f22505d0ad805b40f951d672cf2853217ced602481764c
6
+ metadata.gz: 7837639dace38af311b85547f997b138812c3bba8d969a356936896f0b1a0be9e12ec22e7b86fbaa705f93a6efd55122d6a739339ad732a619965dfb6d058eaf
7
+ data.tar.gz: aa40106dd2c8e0c18129a60e50965a1e4e09eaf3d8305bb367aeab79166d6dc540636e9d0b771e9a4fb6f4e4c1c812593338a4309a2f10251a03f7f9d5b794a2
data/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ TableTransform is still in pre-release state. This means that its APIs and behav
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0][] (2016-03-05)
10
+ * Supports formulas in columns
11
+ * Helper functions to create formulas
12
+ * Column width estimates format size in calculation
13
+
9
14
  ## [0.3.0][] (2016-02-29)
10
15
  * Added format capability for columns when published as Excel
11
16
  * CodeClimate and test coverage added
@@ -21,5 +26,6 @@ TableTransform is still in pre-release state. This means that its APIs and behav
21
26
 
22
27
  [Semver]: http://semver.org
23
28
  [Unreleased]: https://github.com/jonas-lantto/table_transform/compare/v0.3.0...HEAD
29
+ [0.4.0]: https://github.com/jonas-lantto/table_transform/compare/v0.3.0...v0.4.0
24
30
  [0.3.0]: https://github.com/jonas-lantto/table_transform/compare/v0.2.0...v0.3.0
25
31
  [0.2.0]: https://github.com/jonas-lantto/table_transform/compare/v0.1.0...v0.2.0
data/README.md CHANGED
@@ -86,6 +86,41 @@ A row can access its values by column name e.g. `row['Name']`
86
86
  # Add meta data during add_column
87
87
  t.add_column('Tax', {format: '0.0%'}){|row| 0.25}
88
88
 
89
+ ### Formula
90
+ Formulas can be added to columns. They have to be in US locale.
91
+ Column values will be nil if inspected
92
+
93
+ # Add formula to column
94
+ t.add_column_formula('OnePlusOne', '1+1')
95
+
96
+ # Add formula to column with meta data
97
+ t.add_column_formula('OnePlusOne', '1+1' {format: '0.0'})
98
+
99
+ #### Formula helper
100
+ To aid when creating formulas there are a few helpers available
101
+
102
+ f = TableTransform::FormulaHelper # Namespace alias
103
+
104
+ # Column
105
+ f::column('price') # <=> [price]
106
+ t.add_column_formula('Total value', "#{f::column('price')}*f::column('volume')}")
107
+
108
+ # Table
109
+ f::table('Table1') # <=> Table1[]
110
+
111
+ # Text (will avoid the clutter of protecting " character)
112
+ f::text('No') # <=> "No"
113
+
114
+ # VLOOKUP, convenience function to extract values from another table
115
+ # Finds the street name in table Address for name specified in column Name
116
+ f::vlookup(f::column('Name'), 'Address', 'Street'))
117
+ f::vlookup('[Name]', 'Address', 'Street'))
118
+
119
+ # Finds the number of pets in Table Pets for name specified in column Name. If name does not exist it defaults to 0
120
+ f::vlookup(f::column('Name'), 'Pets', 'nPets', 0))
121
+ f::vlookup('[Name]', 'Pets', 'nPets', 0))
122
+
123
+
89
124
  ### Publish
90
125
  # Export as array
91
126
  t.to_a
@@ -23,13 +23,22 @@ module TableTransform
23
23
  #################################
24
24
  private
25
25
 
26
+ # estimated column width of format
27
+ def self.format_column_size(format)
28
+ return 0 if format.nil?
29
+ f = format.gsub(/\[.*?\]/, '').split(';')
30
+ f.map{|x| x.size}.max
31
+ end
32
+
26
33
  # @return array with column width per column
27
- def self.column_width(data, auto_filter_correct = true, max_width = 100)
28
- return [] if data.empty?
34
+ def self.column_width(table, auto_filter_correct = true, max_width = 100)
35
+ return [] unless table.is_a? TableTransform::Table
36
+ data = table.to_a
29
37
 
30
38
  auto_filter_size_correction = auto_filter_correct ? 3 : 0
31
-
32
- res = Array.new(data.first.map { |x| x.to_s.size + auto_filter_size_correction })
39
+ res = Array.new(data.first.map { |name|
40
+ [name.to_s.size + auto_filter_size_correction, format_column_size(table.metadata[name][:format])].max
41
+ })
33
42
  data.each { |row|
34
43
  row.each_with_index { |cell, column_no|
35
44
  res[column_no] = [cell.to_s.size, res[column_no]].max
@@ -45,11 +54,13 @@ module TableTransform
45
54
  }
46
55
  end
47
56
 
48
- def create_column_metadata(metadata, formats)
57
+ def create_column_metadata(metadata, formats, formulas)
49
58
  res = []
50
59
  metadata.each{ |header_name, data|
51
60
  data_dup = data.dup
52
61
  data_dup[:format] = formats[data_dup[:format]] unless data_dup[:format].nil? #replace str format with excel representation
62
+ formula = formulas[header_name]
63
+ data_dup.merge!({formula: formula}) unless formula.nil?
53
64
  res << {header: header_name}.merge(data_dup)
54
65
  }
55
66
  res
@@ -61,7 +72,7 @@ module TableTransform
61
72
  return if data.nil? or data.empty? # Create empty worksheet if no data
62
73
 
63
74
  auto_filter = true
64
- col_width = ExcelCreator::column_width(data, auto_filter)
75
+ col_width = ExcelCreator::column_width(table, auto_filter)
65
76
 
66
77
  header = data.shift
67
78
  data << [nil] * header.count if data.empty? # Add extra row if empty
@@ -72,7 +83,7 @@ module TableTransform
72
83
  :name => name.tr(' ', '_'),
73
84
  :data => data,
74
85
  :autofilter => auto_filter ? 1 : 0,
75
- :columns => create_column_metadata(table.metadata, @formats)
86
+ :columns => create_column_metadata(table.metadata, @formats, table.formulas)
76
87
  }
77
88
  )
78
89
 
@@ -0,0 +1,36 @@
1
+ module TableTransform
2
+
3
+ # Help functions to create formulas
4
+ module FormulaHelper
5
+ # Reference a table
6
+ def self.table(name)
7
+ "#{name}[]"
8
+ end
9
+
10
+ # Reference a column in same table
11
+ def self.column(name)
12
+ "[#{name}]"
13
+ end
14
+
15
+ # Quotes text to be used inside formulas
16
+ def self.text(txt)
17
+ "\"#{txt}\""
18
+ end
19
+
20
+ # vlookup helper, search for a value in another table with return column specified by name
21
+ # Use other help functions to create an excel expression
22
+ #
23
+ # @param [excel expression] search_value, value to lookup
24
+ # @param [string] table_name, name of the table to search in
25
+ # @param [string] return_col_name, name of the return column in given table
26
+ # @param [excel expression] default, value if nothing was found, otherwise Excel will show N/A
27
+ def self.vlookup(search_value, table_name, return_col_name, default = nil)
28
+ vlookup = "VLOOKUP(#{search_value},#{table(table_name)},COLUMN(#{table_name}[[#Headers],#{column(return_col_name)}]),FALSE)"
29
+
30
+ # Workaround
31
+ # Should be possible to write "IFNA(#{vlookup},#{default})"
32
+ # but Excel will error with #Name? until formula is updated by hand
33
+ default.nil? ? vlookup : "IF(ISNA(#{vlookup}),#{default},#{vlookup})"
34
+ end
35
+ end
36
+ end
@@ -8,6 +8,8 @@ module TableTransform
8
8
  end
9
9
 
10
10
  class Table
11
+ attr_reader :formulas
12
+
11
13
  def self.create_from_file(file_name, sep = ',')
12
14
  rows = CSV.read(file_name, { :col_sep => sep })
13
15
  raise "'#{file_name}' contains no data" if rows.empty?
@@ -30,6 +32,7 @@ module TableTransform
30
32
  header = @data_rows.shift
31
33
  @column_indexes = create_column_name_binding(header)
32
34
  @metadata = header.zip( Array.new(header.size){{}} ).to_h
35
+ @formulas = {}
33
36
 
34
37
  validate_header_uniqueness(header)
35
38
  validate_column_size
@@ -50,6 +53,12 @@ module TableTransform
50
53
  @metadata.clone
51
54
  end
52
55
 
56
+ def add_column_formula(column, formula, metadata = {})
57
+ add_column(column, metadata){nil}
58
+ @formulas[column] = formula
59
+ self # self chaining
60
+ end
61
+
53
62
  def << (hash_values)
54
63
  @data_rows << create_row(hash_values)
55
64
  self
@@ -83,6 +92,7 @@ module TableTransform
83
92
  selected_cols = @column_indexes.values_at(*header)
84
93
  t = Table.new( @data_rows.inject([header]) {|res, row| (res << row.values_at(*selected_cols))} )
85
94
  t.metadata = t.metadata.keys.zip(@metadata.values_at(*header)).to_h
95
+ t.formulas = header.zip(@formulas.values_at(*header)).to_h
86
96
  t
87
97
  end
88
98
 
@@ -105,6 +115,7 @@ module TableTransform
105
115
  end
106
116
 
107
117
  def change_column(name)
118
+ raise "Column with formula('#{name}') cannot be changed" if @formulas[name]
108
119
  index = Util::get_col_index(name, @column_indexes)
109
120
  @data_rows.each{|r|
110
121
  r[index] = yield Row.new(@column_indexes, r)
@@ -115,7 +126,10 @@ module TableTransform
115
126
 
116
127
  def delete_column(*names)
117
128
  validate_column_names(*names)
118
- names.each{|n| @metadata.delete(n)}
129
+ names.each{|n|
130
+ @metadata.delete(n)
131
+ @formulas.delete(n)
132
+ }
119
133
 
120
134
  selected_cols = @column_indexes.values_at(*@metadata.keys)
121
135
  @data_rows.map!{|row| row.values_at(*selected_cols)}
@@ -124,6 +138,8 @@ module TableTransform
124
138
  self
125
139
  end
126
140
 
141
+ # Table row
142
+ # Columns within row can be referenced by name, e.g. row['name']
127
143
  class Row
128
144
 
129
145
  def initialize(cols, row)
@@ -140,6 +156,7 @@ module TableTransform
140
156
 
141
157
  end
142
158
 
159
+ # Cell within Table::Row
143
160
  class Cell < String
144
161
  # @returns true if this cell includes any of the given values in list
145
162
  def include_any?(list)
@@ -149,6 +166,7 @@ module TableTransform
149
166
 
150
167
  protected
151
168
  attr_writer :metadata
169
+ attr_writer :formulas
152
170
 
153
171
  private
154
172
  def create_column_name_binding(header_row)
@@ -1,3 +1,3 @@
1
1
  module TableTransform
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -1,3 +1,4 @@
1
1
  require 'table_transform/version'
2
2
  require 'table_transform/table'
3
+ require 'table_transform/formula_helper'
3
4
  require 'table_transform/excel_creator'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: table_transform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Lantto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-02-29 00:00:00.000000000 Z
11
+ date: 2016-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -101,6 +101,7 @@ files:
101
101
  - Rakefile
102
102
  - lib/table_transform.rb
103
103
  - lib/table_transform/excel_creator.rb
104
+ - lib/table_transform/formula_helper.rb
104
105
  - lib/table_transform/table.rb
105
106
  - lib/table_transform/version.rb
106
107
  - table_transform.gemspec