rubyexcel 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,18 +20,18 @@ module RubyExcel
20
20
  def col_letter( index )
21
21
  return index if index.is_a? String
22
22
  index > 0 or fail ArgumentError, 'Indexing is 1-based'
23
- a = 'A' ; return a if index == 1
24
- (index - 1).times{ a.next! }; a
23
+ a = 'A'; ( index - 1 ).times { a.next! }; a
25
24
  end
26
25
 
27
26
  def column_id( address )
28
- address[/[A-Z]+/] or fail ArgumentError, "Invalid address: #{ address }"
27
+ address[/[A-Z]+/]
29
28
  end
30
29
 
31
30
  def expand( address )
32
31
  return [[address]] unless address.include? ':'
33
- start_col, end_col, start_row, end_row = [ address[/^[A-Z]+/], address[/(?<=:)[A-Z]+/] ].sort + [ address.match(/(\d+):/).captures.first, address[/\d+$/] ].sort
34
- (start_row..end_row).map { |r| (start_col..end_col).map { |c| "#{ c }#{ r }" } }
32
+ address.upcase.match( /([A-Z]+)(\d+):([A-Z]+)(\d+)/i )
33
+ start_col, end_col, start_row, end_row = [ $1, $3 ].sort + [ $2.to_i, $4.to_i ].sort
34
+ ( start_row..end_row ).map { |r| ( start_col..end_col ).map { |c| c + r.to_s } }
35
35
  end
36
36
 
37
37
  def indices_to_address( row_idx, column_idx )
@@ -56,7 +56,6 @@ module RubyExcel
56
56
  end
57
57
 
58
58
  def row_id( address )
59
- return nil unless address
60
59
  address[/\d+/].to_i
61
60
  end
62
61
 
@@ -10,8 +10,8 @@ require_relative 'address.rb'
10
10
  include Address
11
11
 
12
12
  def initialize( sheet, input_data )
13
- @sheet = sheet
14
13
  ( input_data.kind_of?( Array ) && input_data.all? { |el| el.kind_of?( Array ) } ) or fail ArgumentError, 'Input must be Array of Arrays'
14
+ @sheet = sheet
15
15
  @data = input_data.dup
16
16
  calc_dimensions
17
17
  end
@@ -21,18 +21,13 @@ require_relative 'address.rb'
21
21
  end
22
22
 
23
23
  def append( multi_array )
24
- @data
25
- @data += multi_array
24
+ @data << multi_array
26
25
  calc_dimensions
27
26
  end
28
27
 
29
28
  def colref_by_header( header )
30
29
  sheet.header_rows > 0 or fail NoMethodError, 'No header rows present'
31
- @data[ 0..sheet.header_rows-1 ].each do |r|
32
- if ( idx = r.index( header ) )
33
- return col_letter( idx+1 )
34
- end
35
- end
30
+ @data[ 0..sheet.header_rows-1 ].each { |r| idx = r.index( header ); return col_letter( idx+1 ) if idx }
36
31
  fail IndexError, "#{ header } is not a valid header"
37
32
  end
38
33
 
@@ -45,13 +40,11 @@ require_relative 'address.rb'
45
40
  ensure_shape
46
41
  @data = @data.transpose.delete_if { |ar| ar.all? { |el| el.to_s.empty? } || ar.empty? }.transpose
47
42
  calc_dimensions
48
- @data
49
43
  end
50
44
 
51
45
  def compact_rows!
52
46
  @data.delete_if { |ar| ar.all? { |el| el.to_s.empty? } || ar.empty? }
53
47
  calc_dimensions
54
- @data
55
48
  end
56
49
 
57
50
  def delete( object )
@@ -69,19 +62,22 @@ require_relative 'address.rb'
69
62
  else
70
63
  fail NoMethodError, "#{ object.class } is not supported"
71
64
  end
72
- self
65
+ calc_dimensions
73
66
  end
74
67
 
75
68
  def delete_column( ref )
76
69
  delete( Column.new( sheet, ref ) )
70
+ calc_dimensions
77
71
  end
78
72
 
79
73
  def delete_row( ref )
80
74
  delete( Row.new( sheet, ref ) )
75
+ calc_dimensions
81
76
  end
82
77
 
83
78
  def delete_range( ref )
84
79
  delete( Element.new( sheet, ref ) )
80
+ calc_dimensions
85
81
  end
86
82
 
87
83
  def dup
@@ -97,35 +93,31 @@ require_relative 'address.rb'
97
93
  idx = col_index( hrows > 0 ? colref_by_header( header ) : header )
98
94
  @data = @data.select.with_index { |row, i| hrows > i || yield( row[ idx -1 ] ) }
99
95
  calc_dimensions
100
- self
101
96
  end
102
97
 
103
98
  def get_columns!( *headers )
99
+ headers = headers.flatten
104
100
  hrow = sheet.header_rows - 1
105
101
  ensure_shape
106
102
  @data = @data.transpose.select{ |col| headers.include?( col[hrow] ) }
107
- ensure_shape
108
103
  @data = @data.sort_by{ |col| headers.index( col[hrow] ) || col[hrow] }.transpose
109
104
  calc_dimensions
110
- self
111
105
  end
112
-
106
+
113
107
  def insert_columns( before, number=1 )
114
108
  a = Array.new( number, nil )
115
109
  before = col_index( before ) - 1
116
110
  @data.map! { |row| row.insert( before, *a ) }
111
+ calc_dimensions
117
112
  end
118
113
 
119
114
  def insert_rows( before, number=1 )
120
115
  @data = @data.insert( ( col_index( before ) - 1 ), *Array.new( number, [nil] ) )
116
+ calc_dimensions
121
117
  end
122
118
 
123
119
  def no_headers
124
- if sheet.header_cols.zero?
125
- @data[ sheet.header_rows..-1 ].dup
126
- else
127
- @data[ sheet.header_rows..-1 ].map { |row| row[ sheet.header_cols..-1 ] }
128
- end
120
+ @data[ sheet.header_rows..-1 ]
129
121
  end
130
122
 
131
123
  def read( addr )
@@ -134,10 +126,27 @@ require_relative 'address.rb'
134
126
  end
135
127
  alias [] read
136
128
 
129
+ def reverse_columns!
130
+ ensure_shape
131
+ @data = @data.transpose.reverse.transpose
132
+ end
133
+
134
+ def reverse_rows!
135
+ @data = skip_headers &:reverse
136
+ end
137
+
138
+ def sort!( &block )
139
+ @data = skip_headers { |d| d.sort( &block ) }; self
140
+ end
141
+
142
+ def sort_by!( &block )
143
+ @data = skip_headers { |d| d.sort_by( &block ) }
144
+ end
145
+
137
146
  def uniq!( header )
138
147
  column = col_index( colref_by_header( header ) )
139
148
  @data = @data.uniq { |row| row[ column - 1 ] }
140
- self
149
+ calc_dimensions
141
150
  end
142
151
  alias unique! uniq!
143
152
 
@@ -158,15 +167,23 @@ require_relative 'address.rb'
158
167
  private
159
168
 
160
169
  def calc_dimensions
161
- @rows, @cols = @data.length, @data.max_by(&:length).length
170
+ @rows, @cols = @data.length, @data.max_by { |row| row.length }.length; self
162
171
  end
163
172
 
164
173
  def ensure_shape
165
174
  calc_dimensions
166
- @data.map! { |ar| ar.length == cols ? ar : ar + Array.new( cols - ar.length, nil) }
167
- @data
175
+ @data = @data.map { |ar| ar.length == cols ? ar : ar + Array.new( cols - ar.length, nil) }
168
176
  end
169
177
 
178
+ def skip_headers
179
+ hr = sheet.header_rows
180
+ if hr > 0
181
+ block_given? ? @data[ 0..hr - 1 ] + yield( @data[ hr..-1 ] ) : @data[ hr..-1 ]
182
+ else
183
+ block_given? ? yield( @data ) : @data
184
+ end
185
+ end
186
+
170
187
  end
171
188
 
172
189
  end
@@ -21,11 +21,7 @@ module RubyExcel
21
21
  include Address
22
22
 
23
23
  def value
24
- if address.include? ':'
25
- expand( address ).map { |ar| ar.map { |addr| data[ addr ] } }
26
- else
27
- data[ address ]
28
- end
24
+ address.include?( ':' ) ? expand( address ).map { |ar| ar.map { |addr| data[ addr ] } } : data[ address ]
29
25
  end
30
26
 
31
27
  def value=( val )
@@ -58,6 +54,10 @@ module RubyExcel
58
54
  def each_cell
59
55
  expand( address ).flatten.each { |addr| yield Element.new( sheet, addr ) }
60
56
  end
57
+
58
+ def empty?
59
+ all? { |v| v.to_s.empty? }
60
+ end
61
61
 
62
62
  def map!
63
63
  expand( address ).flatten.each { |addr| data[ addr ] = yield data[ addr ] }
@@ -1,54 +1,69 @@
1
+ require 'win32ole'
1
2
 
2
- module Excel_Tools
3
+ module ExcelConstants; end
3
4
 
4
- require 'win32ole'
5
+ module RubyExcel
5
6
 
6
- def get_excel
7
- excel = WIN32OLE::connect( 'excel.application' ) rescue WIN32OLE::new( 'excel.application' )
8
- excel.visible = true
9
- excel
10
- end
11
-
12
- def get_workbook( excel=nil )
13
- excel ||= get_excel
14
- wb = excel.workbooks.add
15
- ( ( wb.sheets.count.to_i ) - 1 ).times { |time| wb.sheets(2).delete }
16
- wb
17
- end
18
-
19
- def dump_to_sheet( data, sheet=nil )
20
- fail ArgumentError, "Invalid data type: #{ data.class }" unless data.is_a?( Array ) || data.is_a?( RubyExcel )
21
- data = data.to_a if data.is_a? RubyExcel
22
- sheet ||= get_workbook.sheets(1)
23
- sheet.range( sheet.cells( 1, 1 ), sheet.cells( data.length, data[0].length ) ).value = data
24
- sheet
25
- end
26
-
27
- def make_sheet_pretty( sheet )
28
- c = sheet.cells
29
- c.EntireColumn.AutoFit
30
- c.HorizontalAlignment = -4108
31
- c.VerticalAlignment = -4108
32
- sheet
7
+ def self.borders( range, weight=1, inner=false )
8
+ range.ole_respond_to?( :borders ) or fail ArgumentError, 'First Argument must be WIN32OLE Range'
9
+ [0,1,2,3].include?( weight ) or fail ArgumentError, "Invalid line weight #{ weight }. Must be from 0 to 3"
10
+ defined?( ExcelConstants::XlEdgeLeft ) or WIN32OLE.const_load( range.application, ExcelConstants )
11
+ consts = [ ExcelConstants::XlEdgeLeft, ExcelConstants::XlEdgeTop, ExcelConstants::XlEdgeBottom, ExcelConstants::XlEdgeRight, ExcelConstants::XlInsideVertical, ExcelConstants::XlInsideHorizontal ]
12
+ inner or consts.pop(2)
13
+ weight = [ 0, ExcelConstants::XlThin, ExcelConstants::XlMedium, ExcelConstants::XlThick ][ weight ]
14
+ consts.each { |const| weight.zero? ? range.Borders( const ).linestyle = ExcelConstants::XlNone : range.Borders( const ).weight = weight }
15
+ range
33
16
  end
17
+
18
+ class Workbook
34
19
 
35
- def to_excel
36
- fail NoMethodError, 'Duplicate sheet name' if self.sheets.count != self.sheets.map(&:name).uniq.length
37
- wb = get_workbook
38
- wb.parent.DisplayAlerts = false
39
- first_time = true
40
- self.each do |s|
41
- sht = ( first_time ? wb.sheets(1) : wb.sheets.add( { 'after' => wb.sheets( wb.sheets.count ) } ) ); first_time = false
42
- sht.name = s.name
43
- make_sheet_pretty( dump_to_sheet( s.data.all, sht ) )
20
+ def dump_to_sheet( data, sheet=nil )
21
+ data.is_a?( Array ) or fail ArgumentError, "Invalid data type: #{ data.class }"
22
+ sheet ||= get_workbook.sheets(1)
23
+ sheet.range( sheet.cells( 1, 1 ), sheet.cells( data.length, data[0].length ) ).value = data
24
+ sheet
25
+ end
26
+
27
+ def get_excel
28
+ excel = WIN32OLE::connect( 'excel.application' ) rescue WIN32OLE::new( 'excel.application' )
29
+ excel.visible = true
30
+ excel
31
+ end
32
+
33
+ def get_workbook( excel=nil )
34
+ excel ||= get_excel
35
+ wb = excel.workbooks.add
36
+ ( ( wb.sheets.count.to_i ) - 1 ).times { |time| wb.sheets(2).delete }
37
+ wb
44
38
  end
45
- wb.sheets(1).select
46
- wb
47
- end
48
39
 
49
- def save_excel( filename = 'Output.xlsx' )
50
- filename = Dir.pwd.gsub('/','\\') + '\\' + filename unless filename.include?('\\')
51
- to_excel.saveas filename
40
+ def make_sheet_pretty( sheet )
41
+ c = sheet.cells
42
+ c.entireColumn.autoFit
43
+ c.horizontalAlignment = -4108
44
+ c.verticalAlignment = -4108
45
+ sheet
46
+ end
47
+
48
+ def save_excel( filename = 'Output.xlsx' )
49
+ filename = Dir.pwd.gsub('/','\\') + '\\' + filename unless filename.include?('\\')
50
+ to_excel.saveas filename
51
+ end
52
+
53
+ def to_excel
54
+ self.sheets.count == self.sheets.map(&:name).uniq.length or fail NoMethodError, 'Duplicate sheet name'
55
+ wb = get_workbook
56
+ wb.parent.displayAlerts = false
57
+ first_time = true
58
+ self.each do |s|
59
+ sht = ( first_time ? wb.sheets(1) : wb.sheets.add( { 'after' => wb.sheets( wb.sheets.count ) } ) ); first_time = false
60
+ sht.name = s.name
61
+ make_sheet_pretty( dump_to_sheet( s.data.all, sht ) )
62
+ end
63
+ wb.sheets(1).select
64
+ wb
65
+ end
66
+
52
67
  end
53
68
 
54
69
  end
@@ -6,7 +6,16 @@ require_relative 'section.rb'
6
6
  module RubyExcel
7
7
 
8
8
  def self.sample_data
9
- a=[];8.times{|t|b=[];c='A';5.times{b<<"#{c}#{t+1}";c.next!};a<<b};a
9
+ [
10
+ [ 'Part', 'Ref1', 'Ref2', 'Qty', 'Cost' ],
11
+ [ 'Type1', 'QT1', '231', 1, 35.15 ],
12
+ [ 'Type2', 'QT3', '123', 1, 40 ],
13
+ [ 'Type3', 'XT1', '321', 3, 0.1 ],
14
+ [ 'Type1', 'XY2', '132', 1, 30.00 ],
15
+ [ 'Type4', 'XT3', '312', 2, 3 ],
16
+ [ 'Type2', 'QY2', '213', 1, 99.99 ],
17
+ [ 'Type1', 'QT4', '123', 2, 104 ]
18
+ ]
10
19
  end
11
20
 
12
21
  def self.sample_sheet
@@ -43,8 +43,7 @@ module RubyExcel
43
43
  alias [] read
44
44
 
45
45
  def summarise
46
- h = Hash.new(0)
47
- each_wh { |v| h[v]+=1 }; h
46
+ each_wh.inject( Hash.new(0) ) { |h, v| h[v]+=1; h }
48
47
  end
49
48
  alias summarize summarise
50
49
 
@@ -75,6 +74,12 @@ module RubyExcel
75
74
  each_address { |addr| yield Element.new( sheet, addr ) }
76
75
  end
77
76
 
77
+ def each_cell_without_headers
78
+ return to_enum(:each_cell) unless block_given?
79
+ each_address { |addr| yield Element.new( sheet, addr ) }
80
+ end
81
+ alias each_cell_wh each_cell_without_headers
82
+
78
83
  def map!
79
84
  return to_enum(:map!) unless block_given?
80
85
  each_address { |addr| data[addr] = ( yield data[addr] ) }
@@ -84,8 +89,7 @@ module RubyExcel
84
89
 
85
90
  def translate_address( addr )
86
91
  case self
87
- when Row
88
- col_letter( addr ) + idx.to_s
92
+ when Row ; col_letter( addr ) + idx.to_s
89
93
  when Column
90
94
  addr = addr.to_s unless addr.is_a?( String )
91
95
  fail ArgumentError, "Invalid address : #{ addr }" if addr =~ /[^\d]/
@@ -107,6 +111,11 @@ module RubyExcel
107
111
  def getref( header )
108
112
  column_id( sheet.row(1).find &/#{header}/ )
109
113
  end
114
+
115
+ def value_by_header( header )
116
+ self[ getref( header ) ]
117
+ end
118
+ alias val value_by_header
110
119
 
111
120
  private
112
121
 
data/lib/rubyexcel.rb CHANGED
@@ -17,53 +17,42 @@ module RubyExcel
17
17
 
18
18
  def <<( other )
19
19
  case other
20
- when Workbook
21
- other.inject( @sheets, :<< )
22
- when Sheet
23
- @sheets << other
20
+ when Workbook ; other.each { |sht| sht.workbook = self; @sheets << sht }
21
+ when Sheet ; @sheets << other; other.workbook = self
22
+ when Array ; @sheets << add.load( other )
23
+ else ; fail TypeError, "Unsupported Type: #{ other.class }"
24
24
  end
25
+ self
25
26
  end
26
27
 
27
28
  def add( ref=nil )
28
29
  case ref
29
- when nil
30
- s = Sheet.new( 'Sheet' + ( @sheets.count + 1 ).to_s, self )
31
- when Sheet
32
- s = ref
33
- s.workbook = self
34
- when String
35
- s = Sheet.new( ref, self )
36
- else
37
- fail TypeError, "Unsupported Type: #{ ref.class }"
30
+ when nil ; s = Sheet.new( 'Sheet' + ( @sheets.count + 1 ).to_s, self )
31
+ when Sheet ; ( s = ref ).workbook = self
32
+ when String ; s = Sheet.new( ref, self )
33
+ else ; fail TypeError, "Unsupported Type: #{ ref.class }"
38
34
  end
39
- @sheets << s
40
- s
35
+ @sheets << s; s
41
36
  end
42
37
  alias add_sheet add
43
38
 
44
39
  def clear_all
45
- @sheets = []
46
- self
40
+ @sheets = []; self
47
41
  end
48
42
 
49
43
  def delete( ref )
50
44
  case ref
51
- when Fixnum
52
- @sheets.delete_at( ref - 1 )
53
- when String
54
- @sheets.reject! { |s| s.name == ref }
55
- when Regexp
56
- @sheets.reject! { |s| s.name =~ ref }
57
- when Sheet
58
- @sheets.reject! { |s| s == ref }
59
- else
60
- fail ArgumentError, "Unrecognised Argument Type: #{ ref.class }"
61
- end
45
+ when Fixnum ; @sheets.delete_at( ref - 1 )
46
+ when String ; @sheets.reject! { |s| s.name == ref }
47
+ when Regexp ; @sheets.reject! { |s| s.name =~ ref }
48
+ when Sheet ; @sheets.reject! { |s| s == ref }
49
+ else ; fail ArgumentError, "Unrecognised Argument Type: #{ ref.class }"
50
+ end ; self
62
51
  end
63
52
 
64
53
  def dup
65
54
  wb = Workbook.new
66
- self.each {|s| wb.add s.dup }
55
+ self.each { |s| wb.add s.dup }
67
56
  wb
68
57
  end
69
58
 
@@ -80,14 +69,13 @@ module RubyExcel
80
69
  ref.is_a?( Fixnum ) ? @sheets[ ref - 1 ] : @sheets.find { |s| s.name =~ /^#{ ref }$/i }
81
70
  end
82
71
 
72
+ def sort!
73
+ @sheets = @sheets.sort(&block)
74
+ end
75
+
83
76
  def sort_by!( &block )
84
- if block_given?
85
- @sheets = @sheets.sort_by(&block)
86
- else
87
- @sheets = @sheets.sort_by(&:name)
88
- end
77
+ @sheets = @sheets.sort_by(&block)
89
78
  end
90
- alias sort! sort_by!
91
79
 
92
80
  include Enumerable
93
81
 
@@ -96,22 +84,21 @@ module RubyExcel
96
84
  @sheets.each { |s| yield s }
97
85
  end
98
86
 
99
- include Excel_Tools
100
-
101
87
  end
102
88
 
103
89
  class Sheet
104
90
 
105
91
  attr_reader :data
106
- attr_accessor :name, :header_rows, :header_cols, :workbook
107
- alias parent workbook
92
+ attr_accessor :name, :header_rows, :workbook
93
+ alias parent workbook; alias parent= workbook=
94
+ alias headers header_rows; alias headers= header_rows=
108
95
 
109
96
  include Address
110
97
 
111
98
  def initialize( name, workbook )
112
99
  @workbook = workbook
113
100
  @name = name
114
- @header_rows, @header_cols = nil, nil
101
+ @header_rows = nil
115
102
  @data = Data.new( self, [[]] )
116
103
  end
117
104
 
@@ -130,23 +117,18 @@ module RubyExcel
130
117
 
131
118
  def -( other )
132
119
  case other
133
- when Array
134
- Workbook.new.load( data.all - other )
135
- when Sheet
136
- Workbook.new.load( data.all - other.data.no_headers )
137
- else
138
- fail ArgumentError, "Unsupported class: #{ other.class }"
120
+ when Array ; Workbook.new.load( data.all - other )
121
+ when Sheet ; Workbook.new.load( data.all - other.data.no_headers )
122
+ else ; fail ArgumentError, "Unsupported class: #{ other.class }"
139
123
  end
140
124
  end
141
125
 
142
126
  def <<( other )
143
127
  case other
144
- when Array
145
- load( data.all + other, header_rows, header_cols )
146
- when Sheet
147
- load( data.all + other.data.no_headers, header_rows, header_cols )
148
- else
149
- fail ArgumentError, "Unsupported class: #{ other.class }"
128
+ when Array ; load( data.all + other, header_rows )
129
+ when Hash ; load( data.all + _convert_hash( other ) )
130
+ when Sheet ; load( data.all + other.data.no_headers, header_rows )
131
+ else ; fail ArgumentError, "Unsupported class: #{ other.class }"
150
132
  end
151
133
  end
152
134
 
@@ -165,26 +147,32 @@ module RubyExcel
165
147
  alias ch column_by_header
166
148
 
167
149
  def columns( start_column = 'A', end_column = data.cols )
168
- start_column, end_column = col_letter( start_column ), col_letter( end_column )
169
150
  return to_enum(:columns, start_column, end_column) unless block_given?
170
- ( start_column..end_column ).each { |idx| yield column( idx ) }
151
+ ( col_letter( start_column )..col_letter( end_column ) ).each { |idx| yield column( idx ) }; self
171
152
  end
172
153
 
173
154
  def compact!
174
- data.compact!
175
- self
155
+ data.compact!; self
176
156
  end
177
157
 
178
158
  def delete
179
159
  workbook.delete self
180
160
  end
181
161
 
162
+ def delete_rows_if
163
+ rows.reverse_each { |r| r.delete if yield r }; self
164
+ end
165
+
166
+ def delete_columns_if
167
+ columns.reverse_each { |c| c.delete if yield c }; self
168
+ end
169
+
182
170
  def dup
183
171
  s = Sheet.new( name, workbook )
184
172
  d = data
185
173
  unless d.nil?
186
174
  d = d.dup
187
- s.load( d.all, header_rows, header_cols )
175
+ s.load( d.all, header_rows )
188
176
  d.sheet = s
189
177
  end
190
178
  s
@@ -199,41 +187,36 @@ module RubyExcel
199
187
  end
200
188
 
201
189
  def filter!( ref, &block )
202
- data.filter!( ref, &block )
203
- self
190
+ data.filter!( ref, &block ); self
204
191
  end
205
192
 
206
193
  def get_columns( *headers )
207
- s = dup
208
- s.data.get_columns!( *headers )
209
- s
194
+ dup.data.get_columns!( *headers )
210
195
  end
211
196
  alias gc get_columns
212
197
 
213
198
  def get_columns!( *headers )
214
- data.get_columns!( *headers )
215
- self
199
+ data.get_columns!( *headers ); self
216
200
  end
217
201
  alias gc! get_columns!
218
202
 
219
203
  def insert_columns( *args )
220
- data.insert_columns( *args )
221
- self
204
+ data.insert_columns( *args ); self
222
205
  end
223
206
 
224
207
  def insert_rows( *args )
225
- data.insert_rows( *args )
226
- self
208
+ data.insert_rows( *args ); self
227
209
  end
228
210
 
229
211
  def inspect
230
212
  "#{ self.class }:0x#{ '%x' % (object_id << 1) }: #{ name }"
231
213
  end
232
214
 
233
- def load( input_data, header_rows=1, header_cols=0 )
234
- @header_rows, @header_cols = header_rows, header_cols
235
- @data = Data.new( self, input_data )
236
- self
215
+ def load( input_data, header_rows=1 )
216
+ input_data = _convert_hash(input_data) if input_data.is_a?(Hash)
217
+ input_data.is_a?(Array) or fail ArgumentError, 'Input must be an Array or Hash'
218
+ @header_rows = header_rows
219
+ @data = Data.new( self, input_data ); self
237
220
  end
238
221
 
239
222
  def match( header, &block )
@@ -252,6 +235,14 @@ module RubyExcel
252
235
  def range( first_cell, last_cell=nil )
253
236
  Element.new( self, to_range_address( first_cell, last_cell ) )
254
237
  end
238
+
239
+ def reverse_columns!
240
+ data.reverse_columns!
241
+ end
242
+
243
+ def reverse_rows!
244
+ data.reverse_rows!
245
+ end
255
246
 
256
247
  def row( index )
257
248
  Row.new( self, index )
@@ -259,17 +250,20 @@ module RubyExcel
259
250
 
260
251
  def rows( start_row = 1, end_row = data.rows )
261
252
  return to_enum(:rows, start_row, end_row) unless block_given?
262
- ( start_row..end_row ).each { |idx| yield row( idx ) }
253
+ ( start_row..end_row ).each { |idx| yield row( idx ) }; self
254
+ end
255
+
256
+ def sort!( &block )
257
+ data.sort!( &block ); self
258
+ end
259
+
260
+ def sort_by!( &block )
261
+ data.sort_by!( &block ); self
263
262
  end
264
263
 
265
264
  def sumif( find_header, sum_header )
266
- col1 = column_by_header( find_header )
267
- col2 = column_by_header( sum_header )
268
- total = 0
269
- col1.each_cell do |ce|
270
- total += col2[ ce.row ].to_i if yield( ce.value ) && ce.row >= header_rows
271
- end
272
- total
265
+ find_col, sum_col = ch( find_header ), ch( sum_header )
266
+ find_col.each_cell.inject(0) { |sum,ce| yield( ce.value ) && ce.row > header_rows ? sum + sum_col[ ce.row ] : sum }
273
267
  end
274
268
 
275
269
  def to_a
@@ -281,15 +275,29 @@ module RubyExcel
281
275
  end
282
276
 
283
277
  def to_s
284
- data.nil? ? '' : data.map { |ar| ar.join "\t" }.join( $/ )
278
+ data.nil? ? '' : data.map { |ar| ar.map { |v| v.to_s.gsub(/\t|\n/,' ') }.join "\t" }.join( $/ )
285
279
  end
286
280
 
287
281
  def uniq!( header )
288
- data.uniq!( header )
289
- self
282
+ data.uniq!( header ); self
290
283
  end
291
284
  alias unique! uniq!
292
285
 
286
+ def vlookup( find_header, return_header, &block )
287
+ find_col, return_col = ch( find_header ), ch( return_header )
288
+ return_col[ row_id( find_col.find( &block ) ) ] rescue nil
289
+ end
290
+
291
+ private
292
+
293
+ def _hash_to_a(h)
294
+ h.map { |k,v| v.is_a?(Hash) ? _hash_to_a(v).map { |val| ([ k ] + [ val ]).flatten(1) } : [ k, v ] }.flatten(1)
295
+ end
296
+
297
+ def _convert_hash(h)
298
+ _hash_to_a(h).each_slice(2).map { |a1,a2| a1 << a2.last }
299
+ end
300
+
293
301
  end
294
302
 
295
303
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyexcel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-02 00:00:00.000000000 Z
12
+ date: 2013-04-10 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: A tabular data structure, mixing Ruby with some of Excel's API.
15
- email:
14
+ description: A tabular data structure, mixing Ruby with some of Excel's API style.
15
+ email: VirtuosoJoel@gmail.com
16
16
  executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
- - lib/rubyexcel.rb
21
- - lib/rubyexcel/excel_tools.rb
22
- - lib/rubyexcel/rubyexcel_components.rb
20
+ - lib/rubyexcel/address.rb
23
21
  - lib/rubyexcel/data.rb
24
22
  - lib/rubyexcel/element.rb
23
+ - lib/rubyexcel/excel_tools.rb
24
+ - lib/rubyexcel/rubyexcel_components.rb
25
25
  - lib/rubyexcel/section.rb
26
- - lib/rubyexcel/address.rb
27
- homepage:
26
+ - lib/rubyexcel.rb
27
+ homepage: https://github.com/VirtuosoJoel
28
28
  licenses: []
29
29
  post_install_message:
30
30
  rdoc_options: []