tb 0.3 → 0.4
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/README +2 -1
- data/lib/tb.rb +7 -3
- data/lib/tb/basic.rb +1 -1
- data/lib/tb/cmd_cat.rb +1 -3
- data/lib/tb/cmd_consecutive.rb +4 -6
- data/lib/tb/cmd_crop.rb +5 -7
- data/lib/tb/cmd_cross.rb +51 -49
- data/lib/tb/cmd_cut.rb +2 -6
- data/lib/tb/cmd_git_log.rb +20 -11
- data/lib/tb/cmd_grep.rb +1 -3
- data/lib/tb/cmd_group.rb +18 -44
- data/lib/tb/cmd_gsub.rb +2 -4
- data/lib/tb/cmd_join.rb +1 -3
- data/lib/tb/cmd_ls.rb +8 -15
- data/lib/tb/cmd_mheader.rb +3 -4
- data/lib/tb/cmd_nest.rb +4 -9
- data/lib/tb/cmd_newfield.rb +1 -3
- data/lib/tb/cmd_rename.rb +2 -4
- data/lib/tb/cmd_shape.rb +2 -3
- data/lib/tb/cmd_sort.rb +3 -5
- data/lib/tb/cmd_svn_log.rb +3 -5
- data/lib/tb/cmd_tar_tvf.rb +2 -4
- data/lib/tb/cmd_to_csv.rb +1 -1
- data/lib/tb/cmd_unnest.rb +1 -3
- data/lib/tb/cmdutil.rb +57 -135
- data/lib/tb/csv.rb +11 -54
- data/lib/tb/customcmp.rb +41 -0
- data/lib/tb/customeq.rb +41 -0
- data/lib/tb/enumerable.rb +225 -435
- data/lib/tb/enumerator.rb +22 -14
- data/lib/tb/ex_enumerable.rb +659 -0
- data/lib/tb/ex_enumerator.rb +102 -0
- data/lib/tb/fileenumerator.rb +2 -2
- data/lib/tb/func.rb +141 -0
- data/lib/tb/json.rb +1 -1
- data/lib/tb/reader.rb +4 -4
- data/lib/tb/search.rb +2 -4
- data/lib/tb/zipper.rb +60 -0
- data/test/test_cmd_cat.rb +40 -0
- data/test/test_cmd_git_log.rb +116 -0
- data/test/test_cmd_ls.rb +90 -0
- data/test/test_cmd_svn_log.rb +87 -0
- data/test/test_cmd_to_csv.rb +14 -0
- data/test/test_cmdutil.rb +25 -10
- data/test/test_csv.rb +10 -0
- data/test/test_customcmp.rb +14 -0
- data/test/test_customeq.rb +20 -0
- data/test/{test_enumerable.rb → test_ex_enumerable.rb} +181 -3
- data/test/test_search.rb +2 -10
- data/test/test_tbenum.rb +3 -3
- data/test/test_zipper.rb +22 -0
- metadata +20 -8
- data/lib/tb/enum.rb +0 -294
- data/lib/tb/pairs.rb +0 -227
- data/test/test_pairs.rb +0 -122
data/test/test_search.rb
CHANGED
@@ -523,11 +523,7 @@ class TestTbPathFinder < Test::Unit::TestCase
|
|
523
523
|
s = Tb::Search::EmptyState
|
524
524
|
assert_equal("foo", s.fetch(:k) {|k| assert_equal(:k, k); "foo" })
|
525
525
|
assert_equal("bar", s.fetch(:k, "bar"))
|
526
|
-
|
527
|
-
assert_raise(KeyError) { s.fetch(:k) } # Ruby 1.9
|
528
|
-
else
|
529
|
-
assert_raise(IndexError) { s.fetch(:k) } # Ruby 1.8
|
530
|
-
end
|
526
|
+
assert_raise(KeyError) { s.fetch(:k) }
|
531
527
|
end
|
532
528
|
|
533
529
|
def test_emptystate_values_at
|
@@ -555,11 +551,7 @@ class TestTbPathFinder < Test::Unit::TestCase
|
|
555
551
|
s = Tb::Search::State.make(:k => 1)
|
556
552
|
assert_equal(1, s.fetch(:k))
|
557
553
|
assert_equal(:foo, s.fetch(:x) {|k| assert_equal(:x, k); :foo })
|
558
|
-
|
559
|
-
assert_raise(KeyError) { s.fetch(:x) } # Ruby 1.9
|
560
|
-
else
|
561
|
-
assert_raise(IndexError) { s.fetch(:x) } # Ruby 1.8
|
562
|
-
end
|
554
|
+
assert_raise(KeyError) { s.fetch(:x) }
|
563
555
|
end
|
564
556
|
|
565
557
|
def test_state_keys
|
data/test/test_tbenum.rb
CHANGED
@@ -49,7 +49,7 @@ class TestTbEnum < Test::Unit::TestCase
|
|
49
49
|
header_proc.call(nil) if header_proc
|
50
50
|
self.each(&block)
|
51
51
|
end
|
52
|
-
obj.extend Tb::
|
52
|
+
obj.extend Tb::Enumerable
|
53
53
|
Dir.mktmpdir {|d|
|
54
54
|
open("#{d}/foo.csv", 'w') {|f|
|
55
55
|
obj.write_to_csv(f)
|
@@ -73,7 +73,7 @@ class TestTbEnum < Test::Unit::TestCase
|
|
73
73
|
header_proc.call(nil) if header_proc
|
74
74
|
self.each(&block)
|
75
75
|
end
|
76
|
-
obj.extend Tb::
|
76
|
+
obj.extend Tb::Enumerable
|
77
77
|
Dir.mktmpdir {|d|
|
78
78
|
open("#{d}/foo.csv", 'w') {|f|
|
79
79
|
obj.write_to_csv(f)
|
@@ -97,7 +97,7 @@ class TestTbEnum < Test::Unit::TestCase
|
|
97
97
|
header_proc.call(nil) if header_proc
|
98
98
|
self.each(&block)
|
99
99
|
end
|
100
|
-
obj.extend Tb::
|
100
|
+
obj.extend Tb::Enumerable
|
101
101
|
Dir.mktmpdir {|d|
|
102
102
|
open("#{d}/foo.csv", 'w') {|f|
|
103
103
|
obj.write_to_csv(f, false)
|
data/test/test_zipper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'tb'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class TestZipper < Test::Unit::TestCase
|
5
|
+
def test_basic
|
6
|
+
z = Tb::Zipper.new([Tb::Func::Sum, Tb::Func::Min])
|
7
|
+
assert_equal([5,2], z.aggregate(z.call(z.start([2,3]), z.start([3,2]))))
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_argerr
|
11
|
+
z = Tb::Zipper.new([Tb::Func::Sum, Tb::Func::Min])
|
12
|
+
assert_raise(ArgumentError) { z.start([]) }
|
13
|
+
assert_raise(ArgumentError) { z.start([1]) }
|
14
|
+
assert_raise(ArgumentError) { z.start([1,2,3]) }
|
15
|
+
assert_raise(ArgumentError) { z.call([1], [3]) }
|
16
|
+
assert_raise(ArgumentError) { z.call([1], [3,4]) }
|
17
|
+
assert_raise(ArgumentError) { z.call([1,2], [3]) }
|
18
|
+
assert_raise(ArgumentError) { z.aggregate([]) }
|
19
|
+
assert_raise(ArgumentError) { z.aggregate([1]) }
|
20
|
+
assert_raise(ArgumentError) { z.aggregate([1,2,3]) }
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
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: 2012-
|
12
|
+
date: 2012-02-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'tb is a manipulation tool for table: CSV, TSV, JSON, etc.
|
15
15
|
|
@@ -20,6 +20,8 @@ description: ! 'tb is a manipulation tool for table: CSV, TSV, JSON, etc.
|
|
20
20
|
|
21
21
|
SQL like operations (join, group, etc.),
|
22
22
|
|
23
|
+
information extractions (git-log, svn-log, tar-tvf),
|
24
|
+
|
23
25
|
and more.
|
24
26
|
|
25
27
|
'
|
@@ -65,14 +67,17 @@ files:
|
|
65
67
|
- lib/tb/cmdtop.rb
|
66
68
|
- lib/tb/cmdutil.rb
|
67
69
|
- lib/tb/csv.rb
|
68
|
-
- lib/tb/
|
70
|
+
- lib/tb/customcmp.rb
|
71
|
+
- lib/tb/customeq.rb
|
69
72
|
- lib/tb/enumerable.rb
|
70
73
|
- lib/tb/enumerator.rb
|
74
|
+
- lib/tb/ex_enumerable.rb
|
75
|
+
- lib/tb/ex_enumerator.rb
|
71
76
|
- lib/tb/fieldset.rb
|
72
77
|
- lib/tb/fileenumerator.rb
|
78
|
+
- lib/tb/func.rb
|
73
79
|
- lib/tb/json.rb
|
74
80
|
- lib/tb/pager.rb
|
75
|
-
- lib/tb/pairs.rb
|
76
81
|
- lib/tb/pnm.rb
|
77
82
|
- lib/tb/reader.rb
|
78
83
|
- lib/tb/record.rb
|
@@ -80,6 +85,7 @@ files:
|
|
80
85
|
- lib/tb/ropen.rb
|
81
86
|
- lib/tb/search.rb
|
82
87
|
- lib/tb/tsv.rb
|
88
|
+
- lib/tb/zipper.rb
|
83
89
|
- sample/colors.ppm
|
84
90
|
- sample/excel2csv
|
85
91
|
- sample/gradation.pgm
|
@@ -109,6 +115,7 @@ files:
|
|
109
115
|
- test/test_cmd_rename.rb
|
110
116
|
- test/test_cmd_shape.rb
|
111
117
|
- test/test_cmd_sort.rb
|
118
|
+
- test/test_cmd_svn_log.rb
|
112
119
|
- test/test_cmd_tar_tvf.rb
|
113
120
|
- test/test_cmd_to_csv.rb
|
114
121
|
- test/test_cmd_to_json.rb
|
@@ -120,12 +127,13 @@ files:
|
|
120
127
|
- test/test_cmdtty.rb
|
121
128
|
- test/test_cmdutil.rb
|
122
129
|
- test/test_csv.rb
|
123
|
-
- test/
|
130
|
+
- test/test_customcmp.rb
|
131
|
+
- test/test_customeq.rb
|
132
|
+
- test/test_ex_enumerable.rb
|
124
133
|
- test/test_fieldset.rb
|
125
134
|
- test/test_fileenumerator.rb
|
126
135
|
- test/test_json.rb
|
127
136
|
- test/test_pager.rb
|
128
|
-
- test/test_pairs.rb
|
129
137
|
- test/test_pnm.rb
|
130
138
|
- test/test_reader.rb
|
131
139
|
- test/test_record.rb
|
@@ -133,6 +141,7 @@ files:
|
|
133
141
|
- test/test_search.rb
|
134
142
|
- test/test_tbenum.rb
|
135
143
|
- test/test_tsv.rb
|
144
|
+
- test/test_zipper.rb
|
136
145
|
homepage: https://github.com/akr/tb
|
137
146
|
licenses: []
|
138
147
|
post_install_message:
|
@@ -178,6 +187,7 @@ test_files:
|
|
178
187
|
- test/test_cmd_rename.rb
|
179
188
|
- test/test_cmd_shape.rb
|
180
189
|
- test/test_cmd_sort.rb
|
190
|
+
- test/test_cmd_svn_log.rb
|
181
191
|
- test/test_cmd_tar_tvf.rb
|
182
192
|
- test/test_cmd_to_csv.rb
|
183
193
|
- test/test_cmd_to_json.rb
|
@@ -189,12 +199,13 @@ test_files:
|
|
189
199
|
- test/test_cmdtty.rb
|
190
200
|
- test/test_cmdutil.rb
|
191
201
|
- test/test_csv.rb
|
192
|
-
- test/
|
202
|
+
- test/test_customcmp.rb
|
203
|
+
- test/test_customeq.rb
|
204
|
+
- test/test_ex_enumerable.rb
|
193
205
|
- test/test_fieldset.rb
|
194
206
|
- test/test_fileenumerator.rb
|
195
207
|
- test/test_json.rb
|
196
208
|
- test/test_pager.rb
|
197
|
-
- test/test_pairs.rb
|
198
209
|
- test/test_pnm.rb
|
199
210
|
- test/test_reader.rb
|
200
211
|
- test/test_record.rb
|
@@ -202,3 +213,4 @@ test_files:
|
|
202
213
|
- test/test_search.rb
|
203
214
|
- test/test_tbenum.rb
|
204
215
|
- test/test_tsv.rb
|
216
|
+
- test/test_zipper.rb
|
data/lib/tb/enum.rb
DELETED
@@ -1,294 +0,0 @@
|
|
1
|
-
# Copyright (C) 2012 Tanaka Akira <akr@fsij.org>
|
2
|
-
#
|
3
|
-
# Redistribution and use in source and binary forms, with or without
|
4
|
-
# modification, are permitted provided that the following conditions
|
5
|
-
# are met:
|
6
|
-
#
|
7
|
-
# 1. Redistributions of source code must retain the above copyright
|
8
|
-
# notice, this list of conditions and the following disclaimer.
|
9
|
-
# 2. Redistributions in binary form must reproduce the above
|
10
|
-
# copyright notice, this list of conditions and the following
|
11
|
-
# disclaimer in the documentation and/or other materials provided
|
12
|
-
# with the distribution.
|
13
|
-
# 3. The name of the author may not be used to endorse or promote
|
14
|
-
# products derived from this software without specific prior
|
15
|
-
# written permission.
|
16
|
-
#
|
17
|
-
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
18
|
-
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
-
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
21
|
-
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
-
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
23
|
-
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
-
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
26
|
-
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
27
|
-
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
-
|
29
|
-
module Tb::Enum
|
30
|
-
include Enumerable
|
31
|
-
|
32
|
-
def with_header(&header_proc)
|
33
|
-
Enumerator.new {|y|
|
34
|
-
header_and_each(header_proc) {|pairs|
|
35
|
-
y.yield pairs
|
36
|
-
}
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
def with_cumulative_header(&header_proc)
|
41
|
-
Enumerator.new {|y|
|
42
|
-
hset = {}
|
43
|
-
internal_header_proc = lambda {|header0|
|
44
|
-
if header0
|
45
|
-
header0.each {|f|
|
46
|
-
hset[f] = true
|
47
|
-
}
|
48
|
-
end
|
49
|
-
header_proc.call(header0) if header_proc
|
50
|
-
}
|
51
|
-
header_and_each(internal_header_proc) {|pairs|
|
52
|
-
pairs.each {|f, v|
|
53
|
-
if !hset[f]
|
54
|
-
hset[f] = true
|
55
|
-
end
|
56
|
-
}
|
57
|
-
y.yield [pairs, hset.keys.freeze]
|
58
|
-
}
|
59
|
-
}
|
60
|
-
end
|
61
|
-
|
62
|
-
def cat(*ers, &b)
|
63
|
-
ers = [self, *ers]
|
64
|
-
rec = lambda {|y, header|
|
65
|
-
if ers.empty?
|
66
|
-
if header
|
67
|
-
y.set_header header
|
68
|
-
end
|
69
|
-
else
|
70
|
-
last_e = ers.pop
|
71
|
-
last_e.with_header {|last_e_header|
|
72
|
-
if last_e_header && header
|
73
|
-
header = last_e_header | header
|
74
|
-
else
|
75
|
-
header = nil
|
76
|
-
end
|
77
|
-
rec.call(y, header)
|
78
|
-
}.each {|v|
|
79
|
-
y.yield v
|
80
|
-
}
|
81
|
-
end
|
82
|
-
}
|
83
|
-
er = Tb::Enumerator.new {|y|
|
84
|
-
rec.call(y, [])
|
85
|
-
}
|
86
|
-
if block_given?
|
87
|
-
er.each(&b)
|
88
|
-
else
|
89
|
-
er
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# creates a new Tb::Enumerator object which have
|
94
|
-
# new field named by _field_ with the value returned by the block.
|
95
|
-
#
|
96
|
-
# t1 = Tb.new %w[a b], [1, 2], [3, 4]
|
97
|
-
# p t1.newfield("x") {|row| row["a"] + row["b"] + 100 }.to_a
|
98
|
-
# #=> [#<Tb::Pairs: "x"=>103, "a"=>1, "b"=>2>,
|
99
|
-
# # #<Tb::Pairs: "x"=>107, "a"=>3, "b"=>4>]
|
100
|
-
#
|
101
|
-
def newfield(field)
|
102
|
-
Tb::Enumerator.new {|y|
|
103
|
-
self.with_header {|header|
|
104
|
-
if header
|
105
|
-
y.set_header(Tb::FieldSet.normalize([field, *header]))
|
106
|
-
end
|
107
|
-
}.each {|row|
|
108
|
-
keys = row.keys
|
109
|
-
keys = Tb::FieldSet.normalize([field, *keys])
|
110
|
-
vals = row.values
|
111
|
-
vals = [yield(row), *vals]
|
112
|
-
y << Tb::Pairs.new(keys.zip(vals))
|
113
|
-
}
|
114
|
-
}
|
115
|
-
end
|
116
|
-
|
117
|
-
# :call-seq:
|
118
|
-
# table1.natjoin2(table2, missing_value=nil, retain_left=false, retain_right=false)
|
119
|
-
def natjoin2(tbl2, missing_value=nil, retain_left=false, retain_right=false)
|
120
|
-
Tb::Enumerator.new {|y|
|
121
|
-
tbl1 = self
|
122
|
-
header1 = header2 = nil
|
123
|
-
sorted_tbl2 = nil
|
124
|
-
common_header = nil
|
125
|
-
total_header = nil
|
126
|
-
sorted_tbl1 = tbl1.with_header {|h1|
|
127
|
-
header1 = h1
|
128
|
-
sorted_tbl2 = tbl2.with_header {|h2|
|
129
|
-
header2 = h2
|
130
|
-
common_header = header1 & header2
|
131
|
-
total_header = header1 | header2
|
132
|
-
y.set_header total_header
|
133
|
-
}.lazy_map {|pairs|
|
134
|
-
[common_header.map {|f| pairs[f] }, pairs]
|
135
|
-
}.extsort_by {|cv, pairs| cv }.to_fileenumerator
|
136
|
-
}.lazy_map {|pairs|
|
137
|
-
[common_header.map {|f| pairs[f] }, pairs]
|
138
|
-
}.extsort_by {|cv, pairs| cv }.to_fileenumerator
|
139
|
-
sorted_tbl1.open_reader {|t1|
|
140
|
-
sorted_tbl2.open_reader {|t2|
|
141
|
-
t1_eof = t2_eof = false
|
142
|
-
while true
|
143
|
-
begin
|
144
|
-
cv1, pairs1 = t1.peek
|
145
|
-
rescue StopIteration
|
146
|
-
t1_eof = true
|
147
|
-
end
|
148
|
-
begin
|
149
|
-
cv2, pairs2 = t2.peek
|
150
|
-
rescue StopIteration
|
151
|
-
t2_eof = true
|
152
|
-
end
|
153
|
-
break if t1_eof || t2_eof
|
154
|
-
cmp = cv1 <=> cv2
|
155
|
-
if cmp < 0
|
156
|
-
t1.subeach_by {|_cv1, _| _cv1 }.each {|_, _pairs1|
|
157
|
-
if retain_left
|
158
|
-
h = {}
|
159
|
-
total_header.each {|f|
|
160
|
-
h[f] = missing_value if !_pairs1.has_key?(f)
|
161
|
-
}
|
162
|
-
y.yield _pairs1.merge(h)
|
163
|
-
end
|
164
|
-
}
|
165
|
-
elsif 0 < cmp
|
166
|
-
t2.subeach_by {|_cv2, _| _cv2 }.each {|_, _pairs2|
|
167
|
-
if retain_right
|
168
|
-
h = {}
|
169
|
-
total_header.each {|f|
|
170
|
-
h[f] = missing_value if !_pairs2.has_key?(f)
|
171
|
-
}
|
172
|
-
y.yield _pairs2.merge(h)
|
173
|
-
end
|
174
|
-
}
|
175
|
-
else
|
176
|
-
t2_pos = t2.pos
|
177
|
-
t1.subeach_by {|_cv1, _| _cv1 }.each {|_, _pairs1|
|
178
|
-
t2.pos = t2_pos
|
179
|
-
t2.subeach_by {|_cv2, _| _cv2 }.each {|_, _pairs2|
|
180
|
-
pairs = {}
|
181
|
-
_pairs1.each {|f, v| pairs[f] = v }
|
182
|
-
_pairs2.each {|f, v| pairs[f] = v if !pairs.has_key?(f) }
|
183
|
-
y.yield(pairs)
|
184
|
-
}
|
185
|
-
}
|
186
|
-
end
|
187
|
-
end
|
188
|
-
begin
|
189
|
-
cv1, pairs1 = t1.next
|
190
|
-
if retain_left
|
191
|
-
h = {}
|
192
|
-
total_header.each {|f|
|
193
|
-
h[f] = missing_value if !pairs1.has_key?(f)
|
194
|
-
}
|
195
|
-
y.yield pairs1.merge(h)
|
196
|
-
end
|
197
|
-
rescue StopIteration
|
198
|
-
end
|
199
|
-
begin
|
200
|
-
cv2, pairs2 = t2.next
|
201
|
-
if retain_right
|
202
|
-
h = {}
|
203
|
-
total_header.each {|f|
|
204
|
-
h[f] = missing_value if !pairs2.has_key?(f)
|
205
|
-
}
|
206
|
-
y.yield pairs2.merge(h)
|
207
|
-
end
|
208
|
-
rescue StopIteration
|
209
|
-
end
|
210
|
-
}
|
211
|
-
}
|
212
|
-
}
|
213
|
-
end
|
214
|
-
|
215
|
-
# :call-seq:
|
216
|
-
# table1.natjoin2_outer(table2, missing=nil, retain_left=true, retain_right=true)
|
217
|
-
def natjoin2_outer(tbl2, missing_value=nil, retain_left=true, retain_right=true)
|
218
|
-
natjoin2(tbl2, missing_value, retain_left, retain_right)
|
219
|
-
end
|
220
|
-
|
221
|
-
def to_tb
|
222
|
-
tb = Tb.new
|
223
|
-
self.each {|pairs|
|
224
|
-
pairs.each {|k, v|
|
225
|
-
unless tb.has_field? k
|
226
|
-
tb.define_field(k)
|
227
|
-
end
|
228
|
-
}
|
229
|
-
tb.insert pairs
|
230
|
-
}
|
231
|
-
tb
|
232
|
-
end
|
233
|
-
|
234
|
-
def write_to_csv(io, with_header=true)
|
235
|
-
stream = nil
|
236
|
-
header = []
|
237
|
-
fgen = fnew = nil
|
238
|
-
self.with_cumulative_header {|header0|
|
239
|
-
if !with_header
|
240
|
-
stream = true
|
241
|
-
elsif header0
|
242
|
-
stream = true
|
243
|
-
io.puts Tb.csv_encode_row(header0)
|
244
|
-
else
|
245
|
-
stream = false
|
246
|
-
fgen, fnew = Tb::FileEnumerator.gen_new
|
247
|
-
end
|
248
|
-
}.each {|pairs, header1|
|
249
|
-
pairs = Tb::Pairs.new(pairs) unless pairs.respond_to? :has_key?
|
250
|
-
header = header1
|
251
|
-
if stream
|
252
|
-
fs = header.dup
|
253
|
-
while !fs.empty? && !pairs.has_key?(fs.last)
|
254
|
-
fs.pop
|
255
|
-
end
|
256
|
-
ary = fs.map {|f| pairs[f] }
|
257
|
-
io.puts Tb.csv_encode_row(ary)
|
258
|
-
else
|
259
|
-
fgen.call Tb::Pairs.new(pairs)
|
260
|
-
end
|
261
|
-
}
|
262
|
-
if !stream
|
263
|
-
if with_header
|
264
|
-
io.puts Tb.csv_encode_row(header)
|
265
|
-
end
|
266
|
-
fnew.call.each {|pairs|
|
267
|
-
fs = header.dup
|
268
|
-
while !fs.empty? && !pairs.has_key?(fs.last)
|
269
|
-
fs.pop
|
270
|
-
end
|
271
|
-
ary = fs.map {|f| pairs[f] }
|
272
|
-
io.puts Tb.csv_encode_row(ary)
|
273
|
-
}
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def extsort_by(opts={}, &cmpvalue_from)
|
278
|
-
Tb::Enumerator.new {|ty|
|
279
|
-
header = []
|
280
|
-
er = Enumerator.new {|y|
|
281
|
-
self.with_cumulative_header {|header0|
|
282
|
-
header = header0 if header0
|
283
|
-
}.each {|pairs, header1|
|
284
|
-
header = header1
|
285
|
-
y.yield pairs
|
286
|
-
}
|
287
|
-
ty.set_header header
|
288
|
-
}
|
289
|
-
er.extsort_by(opts, &cmpvalue_from).each {|pairs|
|
290
|
-
ty.yield pairs
|
291
|
-
}
|
292
|
-
}
|
293
|
-
end
|
294
|
-
end
|