lib/tafel.rb in tafel-0.1.0 vs lib/tafel.rb in tafel-0.3.0
- old
+ new
@@ -23,37 +23,160 @@
#++
module Tafel
- VERSION = '0.1.0'
+ VERSION = '0.3.0'
- def self.turn(data)
+ def self.table?(o)
- data = to_array(data)
+ o.is_a?(Array) && o.all? { |r| r.is_a?(Array) }
+ end
- if data.all? { |row| row.is_a?(Array) }
- data
- elsif data.all? { |row| row.is_a?(Hash) }
- turn_array_of_hashes(data)
+ # .to_vtable: keys are arrayed vertically (y explosion)
+ # .to_htable: keys are arrayed horizontally (x explosion)
+
+ def self.to_vtable(x, limit=-1)
+
+ return x if limit == 0
+
+ case x
+ when Hash then x.to_a.collect { |k, v| [ k, to_vtable(v, limit - 1) ] }
+ when Array then x.collect { |e| [ to_vtable(e) ] }
+ else x
+ end
+ end
+
+ def self.to_htable(x)
+
+ kla0 = narrow_class(x)
+
+ kla1 = nil
+ if kla0
+ vs = x.respond_to?(:values) ? x.values : x
+ kla = narrow_class(vs.first)
+ kla1 = vs.all? { |v| kla ? v.is_a?(kla) : false } ? kla : nil
+ end
+
+#p [ kla0, kla1 ]
+ case [ kla0, kla1 ]
+ when [ Hash, Hash ] then to_h_hash_hash(x)
+ when [ Array, Hash ] then to_h_array_hash(x)
+ when [ Hash, nil ] then to_h_hash(x)
+ else x
+ end
+ end
+
+ class << self
+ alias to_v to_vtable
+ alias to_h to_htable
+ end
+
+ def self.flatten(table)
+
+ fail ArgumentError.new('not a table') unless table?(table)
+
+ flat = true
+
+ table =
+ table.collect { |r|
+ r.collect { |c| next c unless table?(c); flat = false; flatten(c) }
+ }
+
+ return table if flat
+
+ ss = table.collect { |r| r.collect { |c| size(c) } }
+
+ ss.each do |row|
+ maxh = row.collect { |cell| cell[1] }.max
+ maxh = maxh < 1 ? 1 : maxh
+ row.each { |cell| cell[1] = maxh }
+ end
+ ss.collect { |row| row.size }.max.times do |x|
+ maxw = ss.collect { |row| cell = row[x]; cell ? cell[0] : 1 }.max
+ maxw = maxw < 1 ? 1 : maxw
+ ss.each { |row| cell = row[x]; cell[0] = maxw if cell }
+ end
+
+ w = ss.first.collect(&:first).reduce(&:+)
+ h = ss.collect { |row| row[0].last }.reduce(&:+)
+
+ a = Array.new(h) { Array.new(w) }
+
+ iterate(ss) do |x, y, s|
+
+ left = x > 0 ? ss[y][x - 1] : nil
+ above = y > 0 ? ss[y - 1][x] : nil
+
+ woff = left ? left[2] + left[0] : 0
+ hoff = above ? above[3] + above[1] : 0
+
+ s.push(woff, hoff)
+
+ copy(a, woff, hoff, table[y][x])
+ end
+
+ a
+ end
+
+ protected # well...
+
+ def self.size(o)
+
+ table?(o) ? [ o.collect { |r| r.size }.max, o.size ] : [ 0, 0 ]
+ end
+
+ def self.copy(target, woff, hoff, source)
+
+ if table?(source)
+ iterate(source) { |x, y, v| target[hoff + y][woff + x] = v }
else
- nil
+ target[hoff][woff] = source
end
end
- protected
+ def self.iterate(table)
- def self.to_array(data)
+ table.first.size.times do |x|
+ table.size.times do |y|
+ yield(x, y, table[y][x])
+ end
+ end
+ end
- data
- #return data.values if data.is_a?(Hash)
- #return Array[data]
+ def self.narrow_class(x)
+
+ return Array if x.is_a?(Array)
+ return Hash if x.is_a?(Hash)
+ nil
end
- def self.turn_array_of_hashes(data)
+ def self.to_h_hash_hash(h)
- keys = data.inject([]) { |a, row| a.concat(row.keys) }.uniq
+ keys = h.values.inject([ :key ]) { |ks, v| ks.concat(v.keys) }.uniq
+ table = [ keys ]
- [ keys ] + data.collect { |row| keys.collect { |k| row[k] } }
+ h.each do |k, v|
+ table << keys[1..-1].inject([ k ]) { |row, key| row << v[key]; row }
+ end
+
+ table
+ end
+
+ def self.to_h_hash(h)
+
+ [ h.keys, h.inject([]) { |a, (k, v)| a << v; a } ]
+ end
+
+ def self.to_h_array_hash(a)
+
+ keys = a.inject([]) { |ks, h| ks.concat(h.keys) }.uniq
+ table = [ keys ]
+
+ a.each do |h|
+ table << keys.inject([]) { |row, key| row << h[key]; row }
+ end
+
+ table
end
end