=begin To add: Hasheesh - Done Input multidimensional array - Done Treat the first row as "headers" Allow setting and retreiving ranges? Allow reordering of columns and rows Allow row / col append Allow row / col insertion Allow row / col deletion Allow export to TSV - Done Allow export to MultiD Array - Done Allow mass strip / upcase Allow "removal" of empty cols / rows (compact) Count cols n rows - Done Select column(s) based on header(s) + bang version Skip headers when parsing the contents Get row by header (title) and lookup key Get value by val_header, lookup_header, and lookup_key Find a value and return its address - Done Filter the data to a header and regex (true/false) Unique a column when given a header =end class RubyExcel ColumnRefs = ('A'..'ZZZ').to_a attr_reader :columns, :rows def initialize @rows = 0 @columns = 0 end def []( addr ) data[ addr.upcase ] end def []=( addr, val ) fail ArgumentError 'Invalid address' if addr !~ /^[A-Z]+\d+$/ || addr[ /[A-Z]+/ ] > 'ZZZ' val.nil? ? data.delete( addr ) : data[ addr ] = val calc_dimensions end def row( row ) Row.new( self, row ) end def column( col ) Column.new( self, col ) end def load( multi_array, overwrite=true ) fail ArgumentError 'Must be multidimensional Array' unless multi_array.all? { |el| el.class == Array } if overwrite @data = {} r = '1' else r = ( rows + 1 ).to_s end multi_array.each { |ar| c = 'A'; ar.each { |el| self[ c + r ] = el; c.next! }; r.next! } calc_dimensions true rescue => e puts e false end def calc_dimensions @columns = calc_columns @rows = calc_rows end def calc_columns col_val( data.keys.map { |k| k[ /[A-Z]+/ ] }.max ) rescue 0 end def calc_rows val = data.keys.map { |k| k[ /\d+/ ].to_i }.max val ||= 0 end def col_val ref ref.class == Fixnum ? ColumnRefs[ ref - 1 ] : ColumnRefs.index( ref ) + 1 end def find val data.key val end def to_a each_row.map do |row| row.to_a end end def to_s to_a.map { |ar| ar.map { |el| "#{el}".strip.gsub( /\s/, ' ' ) }.join "\t" }.join $/ end def each_row return to_enum(:each_row) unless block_given? ( 1..rows ).each do |idx| yield row( idx ) end self end def each_column ( 'A'..col_val( columns ) ).each do |idx| yield column( idx ) end self end private def data @data ||= {} end end class Section attr_reader :matrix def initialize( matrix ) @matrix = matrix end def [](item) matrix[ indexes( item ) ] end def []=(item, val) matrix[ indexes( item ) ] = val end include Enumerable def each each_address do |row, col| yield matrix[ "#{col}#{row}" ] end self end end class Row < Section def initialize( matrix, row_id ) @row_id = row_id super matrix end private def indexes( col_id ) "#{col_id}#{@row_id}" end def each_address ( 'A'..matrix.col_val( matrix.columns ) ).each { |col_id| yield "#{col_id}#{@row_id}" } self end end class Column < Section def initialize( matrix, col ) @col_id = col super matrix end private def indexes( row_id ) "#{@col_id}#{row_id}" end def each_address matrix.rows.times { |row_id| yield "#{@col_id}#{row_id + 1}" } self end end if $0 == __FILE__ require 'pp' puts "test" m = RubyExcel.new m['A1'], m['A2'], m['B1'], m['D4'] = 1, 2, 3, 4 row = m.row(1) row['C'] = 5 puts "rows: #{m.rows}" puts "columns: #{m.columns}" row.each {|x| print "#{x} "} puts pp m.to_a puts m.to_s m.load end