tafel 0.1.0 → 0.3.0
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.
- data/CHANGELOG.txt +5 -0
- data/README.md +114 -1
- data/lib/tafel.rb +139 -16
- data/tafel.gemspec +3 -3
- metadata +5 -5
data/CHANGELOG.txt
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,120 @@
|
|
1
1
|
|
2
2
|
# tafel
|
3
3
|
|
4
|
-
|
4
|
+
[](http://travis-ci.org/jmettraux/tafel)
|
5
|
+
[](http://badge.fury.io/rb/tafel)
|
6
|
+
|
7
|
+
A Ruby library to turn pieces of data into arrays of arrays (tables).
|
8
|
+
|
9
|
+
## interface
|
10
|
+
|
11
|
+
### Tafel .to_htable .to_h
|
12
|
+
|
13
|
+
Turns the argument into a table where keys are arrayed horizontally.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
require 'tafel'
|
17
|
+
|
18
|
+
#
|
19
|
+
# hash of hashes
|
20
|
+
|
21
|
+
Tafel.to_h(
|
22
|
+
{
|
23
|
+
'USD' => { code: 'USD', change: 1.0, min: 500, status: 'active' },
|
24
|
+
'EUR' => { code: 'EUR', change: 1.07, status: 'active' },
|
25
|
+
'CHF' => { code: 'CHF', change: 1.08, min: 700 }
|
26
|
+
}
|
27
|
+
)
|
28
|
+
# ==>
|
29
|
+
[
|
30
|
+
[ :key, :code, :change, :min, :status ],
|
31
|
+
[ 'USD', 'USD', 1.0, 500, 'active' ],
|
32
|
+
[ 'EUR', 'EUR', 1.07, nil, 'active' ],
|
33
|
+
[ 'CHF', 'CHF', 1.08, 700, nil ]
|
34
|
+
]
|
35
|
+
|
36
|
+
#
|
37
|
+
# array of hashes
|
38
|
+
|
39
|
+
Tafel.to_htable(
|
40
|
+
[
|
41
|
+
{ a: 1, b: 2 },
|
42
|
+
{ a: 3, b: 4, c: 5 },
|
43
|
+
{ a: 6, c: 7 }
|
44
|
+
]
|
45
|
+
)
|
46
|
+
# ==>
|
47
|
+
[
|
48
|
+
[ :a, :b, :c ],
|
49
|
+
[ 1, 2, nil ],
|
50
|
+
[ 3, 4, 5 ],
|
51
|
+
[ 6, nil, 7 ]
|
52
|
+
]
|
53
|
+
|
54
|
+
#
|
55
|
+
# plain hash
|
56
|
+
|
57
|
+
Tafel.to_htable(
|
58
|
+
{ a: 3, b: 4, c: 5 }
|
59
|
+
)
|
60
|
+
# ==>
|
61
|
+
[
|
62
|
+
[ :a, :b, :c ],
|
63
|
+
[ 3, 4, 5 ]
|
64
|
+
]
|
65
|
+
```
|
66
|
+
|
67
|
+
### Tafel .to_vtable .to_v
|
68
|
+
|
69
|
+
Turns the argument into a table where keys are arrayed vertically.
|
70
|
+
|
71
|
+
It leaves non-array and no-hash instances as is.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
require 'tafel'
|
75
|
+
|
76
|
+
Tafel.to_v(
|
77
|
+
{ interpreter: 'Leo Ferre', song: { name: "C'est extra", year: 1969 } }
|
78
|
+
)
|
79
|
+
# ==>
|
80
|
+
[
|
81
|
+
[ :interpret, 'Leo Ferre' ],
|
82
|
+
[ :song, [ [ :name, "C'est extra" ], [ :year, 1969 ] ] ]
|
83
|
+
]
|
84
|
+
```
|
85
|
+
|
86
|
+
It accepts a limit integer parameter defaulting to -1 (recurse entirely). For example:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
Tafel.to_v(
|
90
|
+
{ interpreter: 'Leo Ferre', song: { name: "C'est extra", year: 1969 } },
|
91
|
+
1
|
92
|
+
)
|
93
|
+
# ==>
|
94
|
+
[
|
95
|
+
[ :interpret, 'Leo Ferre' ],
|
96
|
+
[ :song, { name: "C'est extra", year: 1969 } ]
|
97
|
+
]
|
98
|
+
```
|
99
|
+
|
100
|
+
### Tafel.flatten
|
101
|
+
|
102
|
+
Turns nested tables into a single table.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
require 'tafel'
|
106
|
+
|
107
|
+
Tafel.flatten(
|
108
|
+
[ [ 0, 1 ],
|
109
|
+
[ 2, [ [ 3, 4 ], [ 5, 6 ] ] ] ]
|
110
|
+
)
|
111
|
+
# -->
|
112
|
+
[ [ 0, 1, nil ],
|
113
|
+
[ 2, 3, 4 ],
|
114
|
+
[ nil, 5, 6 ] ]
|
115
|
+
```
|
116
|
+
|
117
|
+
## license
|
5
118
|
|
6
119
|
MIT, see LICENSE.txt
|
7
120
|
|
data/lib/tafel.rb
CHANGED
@@ -25,35 +25,158 @@
|
|
25
25
|
|
26
26
|
module Tafel
|
27
27
|
|
28
|
-
VERSION = '0.
|
28
|
+
VERSION = '0.3.0'
|
29
29
|
|
30
|
-
def self.
|
30
|
+
def self.table?(o)
|
31
31
|
|
32
|
-
|
32
|
+
o.is_a?(Array) && o.all? { |r| r.is_a?(Array) }
|
33
|
+
end
|
34
|
+
|
35
|
+
# .to_vtable: keys are arrayed vertically (y explosion)
|
36
|
+
# .to_htable: keys are arrayed horizontally (x explosion)
|
37
|
+
|
38
|
+
def self.to_vtable(x, limit=-1)
|
39
|
+
|
40
|
+
return x if limit == 0
|
41
|
+
|
42
|
+
case x
|
43
|
+
when Hash then x.to_a.collect { |k, v| [ k, to_vtable(v, limit - 1) ] }
|
44
|
+
when Array then x.collect { |e| [ to_vtable(e) ] }
|
45
|
+
else x
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.to_htable(x)
|
50
|
+
|
51
|
+
kla0 = narrow_class(x)
|
52
|
+
|
53
|
+
kla1 = nil
|
54
|
+
if kla0
|
55
|
+
vs = x.respond_to?(:values) ? x.values : x
|
56
|
+
kla = narrow_class(vs.first)
|
57
|
+
kla1 = vs.all? { |v| kla ? v.is_a?(kla) : false } ? kla : nil
|
58
|
+
end
|
59
|
+
|
60
|
+
#p [ kla0, kla1 ]
|
61
|
+
case [ kla0, kla1 ]
|
62
|
+
when [ Hash, Hash ] then to_h_hash_hash(x)
|
63
|
+
when [ Array, Hash ] then to_h_array_hash(x)
|
64
|
+
when [ Hash, nil ] then to_h_hash(x)
|
65
|
+
else x
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
alias to_v to_vtable
|
71
|
+
alias to_h to_htable
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.flatten(table)
|
75
|
+
|
76
|
+
fail ArgumentError.new('not a table') unless table?(table)
|
77
|
+
|
78
|
+
flat = true
|
79
|
+
|
80
|
+
table =
|
81
|
+
table.collect { |r|
|
82
|
+
r.collect { |c| next c unless table?(c); flat = false; flatten(c) }
|
83
|
+
}
|
84
|
+
|
85
|
+
return table if flat
|
86
|
+
|
87
|
+
ss = table.collect { |r| r.collect { |c| size(c) } }
|
88
|
+
|
89
|
+
ss.each do |row|
|
90
|
+
maxh = row.collect { |cell| cell[1] }.max
|
91
|
+
maxh = maxh < 1 ? 1 : maxh
|
92
|
+
row.each { |cell| cell[1] = maxh }
|
93
|
+
end
|
94
|
+
ss.collect { |row| row.size }.max.times do |x|
|
95
|
+
maxw = ss.collect { |row| cell = row[x]; cell ? cell[0] : 1 }.max
|
96
|
+
maxw = maxw < 1 ? 1 : maxw
|
97
|
+
ss.each { |row| cell = row[x]; cell[0] = maxw if cell }
|
98
|
+
end
|
99
|
+
|
100
|
+
w = ss.first.collect(&:first).reduce(&:+)
|
101
|
+
h = ss.collect { |row| row[0].last }.reduce(&:+)
|
102
|
+
|
103
|
+
a = Array.new(h) { Array.new(w) }
|
33
104
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
105
|
+
iterate(ss) do |x, y, s|
|
106
|
+
|
107
|
+
left = x > 0 ? ss[y][x - 1] : nil
|
108
|
+
above = y > 0 ? ss[y - 1][x] : nil
|
109
|
+
|
110
|
+
woff = left ? left[2] + left[0] : 0
|
111
|
+
hoff = above ? above[3] + above[1] : 0
|
112
|
+
|
113
|
+
s.push(woff, hoff)
|
114
|
+
|
115
|
+
copy(a, woff, hoff, table[y][x])
|
116
|
+
end
|
117
|
+
|
118
|
+
a
|
119
|
+
end
|
120
|
+
|
121
|
+
protected # well...
|
122
|
+
|
123
|
+
def self.size(o)
|
124
|
+
|
125
|
+
table?(o) ? [ o.collect { |r| r.size }.max, o.size ] : [ 0, 0 ]
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.copy(target, woff, hoff, source)
|
129
|
+
|
130
|
+
if table?(source)
|
131
|
+
iterate(source) { |x, y, v| target[hoff + y][woff + x] = v }
|
38
132
|
else
|
39
|
-
|
133
|
+
target[hoff][woff] = source
|
40
134
|
end
|
41
135
|
end
|
42
136
|
|
43
|
-
|
137
|
+
def self.iterate(table)
|
44
138
|
|
45
|
-
|
139
|
+
table.first.size.times do |x|
|
140
|
+
table.size.times do |y|
|
141
|
+
yield(x, y, table[y][x])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.narrow_class(x)
|
46
147
|
|
47
|
-
|
48
|
-
|
49
|
-
|
148
|
+
return Array if x.is_a?(Array)
|
149
|
+
return Hash if x.is_a?(Hash)
|
150
|
+
nil
|
50
151
|
end
|
51
152
|
|
52
|
-
def self.
|
153
|
+
def self.to_h_hash_hash(h)
|
154
|
+
|
155
|
+
keys = h.values.inject([ :key ]) { |ks, v| ks.concat(v.keys) }.uniq
|
156
|
+
table = [ keys ]
|
157
|
+
|
158
|
+
h.each do |k, v|
|
159
|
+
table << keys[1..-1].inject([ k ]) { |row, key| row << v[key]; row }
|
160
|
+
end
|
161
|
+
|
162
|
+
table
|
163
|
+
end
|
53
164
|
|
54
|
-
|
165
|
+
def self.to_h_hash(h)
|
166
|
+
|
167
|
+
[ h.keys, h.inject([]) { |a, (k, v)| a << v; a } ]
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.to_h_array_hash(a)
|
171
|
+
|
172
|
+
keys = a.inject([]) { |ks, h| ks.concat(h.keys) }.uniq
|
173
|
+
table = [ keys ]
|
174
|
+
|
175
|
+
a.each do |h|
|
176
|
+
table << keys.inject([]) { |row, key| row << h[key]; row }
|
177
|
+
end
|
55
178
|
|
56
|
-
|
179
|
+
table
|
57
180
|
end
|
58
181
|
end
|
59
182
|
|
data/tafel.gemspec
CHANGED
@@ -10,13 +10,13 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.authors = [ 'John Mettraux' ]
|
12
12
|
s.email = [ 'jmettraux@gmail.com' ]
|
13
|
-
s.homepage = 'http://github.com/jmettraux/
|
13
|
+
s.homepage = 'http://github.com/jmettraux/tafel'
|
14
14
|
s.rubyforge_project = 'rufus'
|
15
15
|
s.license = 'MIT'
|
16
|
-
s.summary = 'something to turn data into
|
16
|
+
s.summary = 'something to turn data into arrays of arrays'
|
17
17
|
|
18
18
|
s.description = %{
|
19
|
-
Something to turn data
|
19
|
+
Something to turn data into arrays of arrays (suitable for CSV).
|
20
20
|
}.strip
|
21
21
|
|
22
22
|
#s.files = `git ls-files`.split("\n")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tafel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-05-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -27,7 +27,7 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 2.13.0
|
30
|
-
description: Something to turn data
|
30
|
+
description: Something to turn data into arrays of arrays (suitable for CSV).
|
31
31
|
email:
|
32
32
|
- jmettraux@gmail.com
|
33
33
|
executables: []
|
@@ -40,7 +40,7 @@ files:
|
|
40
40
|
- CHANGELOG.txt
|
41
41
|
- LICENSE.txt
|
42
42
|
- README.md
|
43
|
-
homepage: http://github.com/jmettraux/
|
43
|
+
homepage: http://github.com/jmettraux/tafel
|
44
44
|
licenses:
|
45
45
|
- MIT
|
46
46
|
post_install_message:
|
@@ -64,5 +64,5 @@ rubyforge_project: rufus
|
|
64
64
|
rubygems_version: 1.8.23.2
|
65
65
|
signing_key:
|
66
66
|
specification_version: 3
|
67
|
-
summary: something to turn data into
|
67
|
+
summary: something to turn data into arrays of arrays
|
68
68
|
test_files: []
|