class Tabmani::Table
Attributes
indent[R]
matrix[R]
titles[R]
Public Class Methods
dump_column_format(matrix:, hlines: [], io: $stdout, indent: 0, titles: nil, just: :left, separator: ' ')
click to toggle source
# File lib/tabmani/table.rb, line 74 def self.dump_column_format(matrix:, hlines: [], io: $stdout, indent: 0, titles: nil, just: :left, separator: ' ') matrix = [titles, * matrix] if titles maxima = self.max_lengths(matrix) lines = matrix.map do |row| self.line_str(items: row, lengths: maxima, separator: separator, just: just, indent: indent) end hlines.sort.reverse.each { |index| lines.insert(index, '-' * whole_length(matrix)) } io.puts lines.join("\n") end
get_ranges(ary)
click to toggle source
true の範囲を示す二重配列を返す。 各要素は 始点..終点 の各インデックスで出来た範囲。
各要素は[始点, 終点] の各インデックス。
# File lib/tabmani/table.rb, line 101 def self.get_ranges(ary) results = [] start = nil prev = false ary << false # for true in final item ary.each_with_index do |cur, i| if prev == false && cur == true start = i prev = cur elsif prev == true && cur == false results << (start..(i - 1)) prev = cur else next end end ary.pop results end
line_str(items: , lengths: , lineend: nil, just: :left, indent: 0, separator: ' ')
click to toggle source
# File lib/tabmani/table.rb, line 165 def self.line_str(items: , lengths: , lineend: nil, just: :left, indent: 0, separator: ' ') new_items = [] lengths.each_with_index do |length, index| item = items[index].to_s new_items[index] = self.padding(str: item, width: lengths[index], place: just) end str = " " * indent str += new_items.join(separator) if lineend str += lineend else str.sub!(/ +$/, "") end str end
max_lengths(matrix)
click to toggle source
return array of max size of item at index
# File lib/tabmani/table.rb, line 188 def self.max_lengths(matrix) results = [] matrix.each do |row| row.each_with_index do |item, index| item = item.to_s results[index] ||= 0 size = Tabmani::Table.print_size(item) results[index] = size if results[index] < size end end results end
new(matrix: , indent: 0)
click to toggle source
# File lib/tabmani/table.rb, line 17 def initialize(matrix: , indent: 0) @matrix = matrix @titles = nil @indent = indent @hlines = [] end
num_columns(matrix)
click to toggle source
# File lib/tabmani/table.rb, line 161 def self.num_columns(matrix) matrix.map{|items| items.size}.max end
padding(str: , width: , padding: ' ', place: :left)
click to toggle source
# File lib/tabmani/table.rb, line 141 def self.padding(str: , width: , padding: ' ', place: :left) output_width = str.each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+) padding_size = [0, width - output_width].max case place when :left ; left = 0 ; right = padding_size when :right ; left = padding_size ; right = 0 when :center ; left = padding_size / 2 ; right = padding_size - left else raise SymbolMismatchError, place end (padding * left) + str + (padding * right) end
parse(io: , style: , separator: ',')
click to toggle source
Wrapper for various parse method.
style: :csv, :blank, or :column separator: option for separator style
# File lib/tabmani/table.rb, line 27 def self.parse(io: , style: , separator: ',') case style when :csv ; self.parse_csv(io) when :blank ; self.parse_blanks_separated_value(io) when :column ; self.parse_column_based_value(io) when :separator ; self.parse_separator(io, separator) else; raise ParseError, "Unknown style: #{style}" end end
parse_blanks_separated_value(io)
click to toggle source
blanks_separated_value
# File lib/tabmani/table.rb, line 51 def self.parse_blanks_separated_value(io) lines = io.readlines indent = lines.map{|line| /^(\s*)/ =~ line; $1.length}.min matrix = lines.map { |line| line.strip.split(/\s+/) } lines.map { |line| line.strip.split(/\s+/) } self.new(matrix: matrix, indent: indent) end
parse_column_based_value(io)
click to toggle source
column_based_value
縦に貫通する空白列を区切りにする。 各要素の空白は strip する。
# File lib/tabmani/table.rb, line 64 def self.parse_column_based_value(io) lines = io.readlines return if lines.empty? lines.map! { |line| line.chomp } lines.delete_if { |line| line.empty? } # delete line consist of linefeed only. ranges = self.get_ranges(self.projection_ary(lines)) matrix = lines.map { |line| ranges.map { |range| line[range].to_s.strip} } self.new(matrix: matrix) end
parse_csv(io)
click to toggle source
CSV
# File lib/tabmani/table.rb, line 39 def self.parse_csv(io) self.new(matrix: CSV.parse(io)) end
parse_separator(io, separator)
click to toggle source
# File lib/tabmani/table.rb, line 43 def self.parse_separator(io, separator) lines = io.readlines matrix = lines.map { |line| line.chomp.split(separator) } self.new(matrix: matrix) end
print_size(string)
click to toggle source
# File lib/tabmani/table.rb, line 94 def self.print_size(string) string.each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+) end
projection_ary(lines)
click to toggle source
全ての文字列の最大長を要素数とする配列で、 空白文字以外があれば true, 全て空白文字ならば false にした配列。
# File lib/tabmani/table.rb, line 123 def self.projection_ary(lines) return [] if lines.empty? max_length = lines.max_by{|line| line.size}.size results = Array.new(max_length).fill(false) lines.each do |line| line.chomp.size.times do |i| c = line[i] next if results[i] == true if c == ' ' next else results[i] = true end end end results end
whole_length(matrix)
click to toggle source
# File lib/tabmani/table.rb, line 155 def self.whole_length(matrix) result = 0 self.max_lengths(matrix).each { |l| result += l} result += num_columns(matrix) - 1 end
Public Instance Methods
add_hline(num)
click to toggle source
num is the index of row number below the horizontal line.
# File lib/tabmani/table.rb, line 419 def add_hline(num) @hlines << num end
add_sum(key)
click to toggle source
# File lib/tabmani/table.rb, line 401 def add_sum(key) index = key.to_i - 1 sum = 0 @matrix.each do |items| str = items[index] if str.include?('.') sum += str.to_f else sum += str.to_i end end items = Array.new(num_columns).fill('') items[index] = sum.to_s add_hline( @matrix.size) @matrix << items end
analyze(io: , keys:)
click to toggle source
# File lib/tabmani/table.rb, line 364 def analyze(io: , keys:) io.puts unless @titles @titles = [] num_columns.times do |i| @titles[i] = @matrix[0][i].to_s end end if @matrix.size != 0 results = [] results << %w(key head types) @titles.size.times do |i| results << [(i+1).to_s, @titles[i].strip, @matrix.map {|items| items[i]}.sort_by{|j| j.to_s}.uniq.size.to_s ] end Tabmani::Table.dump_column_format(matrix: results, io: io) end unless keys.empty? io.puts io.puts "key analysis" keys.each do |key| io.puts "(key=#{key})" values = @matrix.map{|items| items[key.to_i-1] } names = values.sort.uniq results = [] names.each { |name| results << [name, values.count(name).to_s] } results.sort_by!{|v| v[1].to_i} Tabmani::Table.dump_column_format(matrix: results, io: io) io.puts end end end
dump(io: , style: , just: :left, separator: ' ')
click to toggle source
Wrapper for various dump method.
# File lib/tabmani/table.rb, line 202 def dump(io: , style: , just: :left, separator: ' ') case style when :column ; dump_column_format(io: io, just: just, separator: separator ) when :csv ; dump_csv(io: io) when :mds ; dump_md_simple(io: io, just: just ) when :tex ; dump_tex(io: io, just: just ) else raise ParseError, "Unknown style: #{style}" end end
dump_column_format(io: $stdout, just: :left, separator: ' ')
click to toggle source
# File lib/tabmani/table.rb, line 213 def dump_column_format(io: $stdout, just: :left, separator: ' ') self.class.dump_column_format(matrix: @matrix, hlines: @hlines, titles: @titles, io: io, indent: @indent, just: just, separator: separator) end
dump_csv(io: )
click to toggle source
# File lib/tabmani/table.rb, line 223 def dump_csv(io: ) matrix = @matrix matrix = [@titles, * @matrix] if @titles csv_string = CSV.generate do |csv| matrix.each { |items| csv << items } end io.print csv_string end
dump_md_simple(io: $stdout, just: :left)
click to toggle source
markdown simple table style
# File lib/tabmani/table.rb, line 233 def dump_md_simple(io: $stdout, just: :left) if @titles matrix = [@titles, * @matrix] if @titles else matrix = @matrix.clone end matrix.insert(1, line_row) self.class.dump_column_format(io: io, matrix: matrix, just: just) end
dump_tex(io: $stdout, just: :left)
click to toggle source
# File lib/tabmani/table.rb, line 243 def dump_tex(io: $stdout, just: :left) case just when :left ; just_char = 'l' when :right ; just_char = 'r' when :center ; just_char = 'c' end maxima = max_lengths h_size = @matrix.map{|row| row.size }.max io.puts "\\begin{tabular}{#{just_char * h_size }}" lines = [] if @titles lines << self.class.line_str(items: @titles, lengths: maxima, separator: " & ", just: just, lineend: ' \\\\', indent: 2) @hlines << 1 end @matrix.each do |row| lines << self.class.line_str(items: row, lengths: maxima, separator: " & ", just: just, lineend: ' \\\\', indent: 2) end #@hlines = [0, maxima.size] if @hlines.empty? @hlines << 0 @hlines << maxima.size @hlines.sort.reverse.each do |index| lines.insert(index, " \\hline") end io.puts lines.join("\n") io.puts "\\end{tabular}" end
filter(key, val)
click to toggle source
# File lib/tabmani/table.rb, line 317 def filter(key, val) result = Marshal.load(Marshal.dump(self)) result.filter!(key, val) result end
filter!(key, val)
click to toggle source
# File lib/tabmani/table.rb, line 311 def filter!(key, val) @matrix.select! do |items| items[key.to_i - 1] == val end end
max_lengths()
click to toggle source
return array of max size of item at index
# File lib/tabmani/table.rb, line 357 def max_lengths matrix = @matrix matrix = [@titles, * @matrix] if @titles self.class.max_lengths(matrix) end
reform(key_x, key_y, key_val)
click to toggle source
# File lib/tabmani/table.rb, line 323 def reform(key_x, key_y, key_val) x_index = key_x .to_i - 1 y_index = key_y .to_i - 1 v_index = key_val.to_i - 1 data_hash = {} xs = [] ys = [] @matrix.each do |items| next if items.empty? x = items[x_index] y = items[y_index] v = items[v_index] data_hash[y] ||= {} raise DuplicateCellError, "Duplicated condition: #{x}, #{y}" if data_hash[y][x] data_hash[y][x] = v xs << x ys << y end xs = xs.sort.uniq ys = ys.sort.uniq matrix = [] matrix << [''] + xs ys.each do |y| items = [] items << y xs.each { |x| items << data_hash[y][x] } matrix << items end @matrix = matrix end
set_title()
click to toggle source
# File lib/tabmani/table.rb, line 430 def set_title @titles = @matrix.shift end
show_keys(io: $stdout, separator: ' ')
click to toggle source
# File lib/tabmani/table.rb, line 284 def show_keys(io: $stdout, separator: ' ') matrix = [] matrix << line_row tmp = [] max_lengths.size.times do |i| tmp << (i + 1).to_s end matrix << tmp self.class.dump_column_format(matrix: matrix, io: io, indent: @indent) end
strip()
click to toggle source
delete spaces head or tail in each items. Destructive
# File lib/tabmani/table.rb, line 424 def strip @matrix.map! do |items| items.map {|str| str.strip} end end
transpose()
click to toggle source
# File lib/tabmani/table.rb, line 305 def transpose result = Marshal.load(Marshal.dump(self)) result.transpose! result end
transpose!()
click to toggle source
transpose matrix. empty cell is filled by empty String, ''. thanks: www.tom08.net/entry/2017/12/21/125127
# File lib/tabmani/table.rb, line 300 def transpose! max_length = @matrix.max_by(&:size).size @matrix = @matrix.map { |m| m.fill('', m.size, max_length - m.size) }.transpose end
Private Instance Methods
line_row()
click to toggle source
# File lib/tabmani/table.rb, line 436 def line_row max_lengths.map {|i| '-' * i} end
num_columns()
click to toggle source
# File lib/tabmani/table.rb, line 440 def num_columns self.class.num_columns(@matrix) end