tafel 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/jmettraux/tafel.svg)](http://travis-ci.org/jmettraux/tafel)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/tafel.svg)](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: []
|