ntable 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +5 -0
- data/Version +1 -1
- data/lib/ntable.rb +1 -0
- data/lib/ntable/construction.rb +32 -14
- data/lib/ntable/index_wrapper.rb +73 -0
- data/lib/ntable/structure.rb +18 -6
- data/lib/ntable/table.rb +21 -4
- data/test/tc_basic_values.rb +21 -0
- data/test/tc_nested_object.rb +16 -0
- metadata +2 -1
data/History.rdoc
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
=== 0.1.3 / 2012-09-10
|
2
|
+
|
3
|
+
* Creating a table from a nested object, using a label conversion procedure, tended to fail with NoSuchCellError. Fixed and added test cases.
|
4
|
+
* Support cell lookup using the 0-based row index as well as the label.
|
5
|
+
|
1
6
|
=== 0.1.2 / 2012-09-09
|
2
7
|
|
3
8
|
I took a long look at ntable's interface, and the usage patterns of my own personal projects that depended on it, and decided some simplification was needed.
|
data/Version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/lib/ntable.rb
CHANGED
data/lib/ntable/construction.rb
CHANGED
@@ -34,6 +34,7 @@
|
|
34
34
|
;
|
35
35
|
|
36
36
|
|
37
|
+
require 'set'
|
37
38
|
require 'json'
|
38
39
|
|
39
40
|
|
@@ -41,6 +42,8 @@ module NTable
|
|
41
42
|
|
42
43
|
|
43
44
|
@numeric_sort = ::Proc.new{ |a_, b_| a_.to_f <=> b_.to_f }
|
45
|
+
@integer_sort = ::Proc.new{ |a_, b_| a_.to_i <=> b_.to_i }
|
46
|
+
@string_sort = ::Proc.new{ |a_, b_| a_.to_s <=> b_.to_s }
|
44
47
|
|
45
48
|
|
46
49
|
class << self
|
@@ -148,22 +151,36 @@ module NTable
|
|
148
151
|
stringify_ = field_[:stringify] || stringify_by_default_
|
149
152
|
objectify_ ||= objectify_by_default_ unless stringify_
|
150
153
|
if objectify_
|
151
|
-
|
152
|
-
|
154
|
+
if objectify_.respond_to?(:call)
|
155
|
+
h_ = ::Set.new
|
156
|
+
ai_.keys.each do |k_|
|
157
|
+
nv_ = objectify_.call(k_)
|
158
|
+
ai_[k_] = nv_
|
159
|
+
h_ << nv_
|
160
|
+
end
|
161
|
+
labels_ = h_.to_a
|
162
|
+
else
|
163
|
+
labels_ = ai_.keys
|
164
|
+
end
|
153
165
|
klass_ = ObjectAxis
|
154
166
|
else
|
155
|
-
h_ = {}
|
156
167
|
stringify_ = nil unless stringify_.respond_to?(:call)
|
157
|
-
|
158
|
-
|
159
|
-
|
168
|
+
h_ = ::Set.new
|
169
|
+
ai_.keys.each do |k_|
|
170
|
+
nv_ = (stringify_ ? stringify_.call(k_) : k_).to_s
|
171
|
+
ai_[k_] = nv_
|
172
|
+
h_ << nv_
|
160
173
|
end
|
161
|
-
labels_ = h_.
|
174
|
+
labels_ = h_.to_a
|
162
175
|
klass_ = LabeledAxis
|
163
176
|
end
|
164
177
|
if (sort_ = field_[:sort])
|
165
178
|
if sort_.respond_to?(:call)
|
166
179
|
func_ = sort_
|
180
|
+
elsif sort_ == :string
|
181
|
+
func_ = @string_sort
|
182
|
+
elsif sort_ == :integer
|
183
|
+
func_ = @integer_sort
|
167
184
|
elsif sort_ == :numeric
|
168
185
|
func_ = @numeric_sort
|
169
186
|
else
|
@@ -178,7 +195,7 @@ module NTable
|
|
178
195
|
struct_.add(axis_, name_) if axis_
|
179
196
|
end
|
180
197
|
table_ = Table.new(struct_, :fill => opts_[:fill])
|
181
|
-
_populate_nested_values(table_, [], obj_)
|
198
|
+
_populate_nested_values(table_, [], axis_data_, obj_)
|
182
199
|
table_
|
183
200
|
end
|
184
201
|
|
@@ -191,16 +208,16 @@ module NTable
|
|
191
208
|
set_ = ai_
|
192
209
|
else
|
193
210
|
set_ = axis_data_[index_] = {}
|
194
|
-
(ai_[0]...ai_[1]).each{ |i_| set_[i_] =
|
211
|
+
(ai_[0]...ai_[1]).each{ |i_| set_[i_] = i_ } if ::Array === ai_
|
195
212
|
end
|
196
213
|
obj_.each do |k_, v_|
|
197
|
-
set_[k_] =
|
214
|
+
set_[k_] = k_
|
198
215
|
_populate_nested_axes(axis_data_, index_+1, v_)
|
199
216
|
end
|
200
217
|
when ::Array
|
201
218
|
if ::Hash === ai_
|
202
219
|
obj_.each_with_index do |v_, i_|
|
203
|
-
ai_[i_] =
|
220
|
+
ai_[i_] = i_
|
204
221
|
_populate_nested_axes(axis_data_, index_+1, v_)
|
205
222
|
end
|
206
223
|
else
|
@@ -222,18 +239,19 @@ module NTable
|
|
222
239
|
end
|
223
240
|
|
224
241
|
|
225
|
-
def _populate_nested_values(table_, path_, obj_) # :nodoc:
|
242
|
+
def _populate_nested_values(table_, path_, axis_data_, obj_) # :nodoc:
|
226
243
|
if path_.size == table_.dim
|
227
244
|
table_.set!(*path_, obj_)
|
228
245
|
else
|
229
246
|
case obj_
|
230
247
|
when ::Hash
|
248
|
+
h_ = axis_data_[path_.size]
|
231
249
|
obj_.each do |k_, v_|
|
232
|
-
_populate_nested_values(table_, path_ + [k_], v_)
|
250
|
+
_populate_nested_values(table_, path_ + [h_[k_]], axis_data_, v_)
|
233
251
|
end
|
234
252
|
when ::Array
|
235
253
|
obj_.each_with_index do |v_, i_|
|
236
|
-
_populate_nested_values(table_, path_ + [i_], v_) unless v_.nil?
|
254
|
+
_populate_nested_values(table_, path_ + [i_], axis_data_, v_) unless v_.nil?
|
237
255
|
end
|
238
256
|
end
|
239
257
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# NTable index wrapper
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2012 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
module NTable
|
38
|
+
|
39
|
+
|
40
|
+
# Use one of these in a coordinate to force the coordinate to be
|
41
|
+
# treated as an index rather than a label.
|
42
|
+
|
43
|
+
class IndexWrapper
|
44
|
+
|
45
|
+
# Create an IndexWrapper with the given integer index
|
46
|
+
|
47
|
+
def initialize(val_)
|
48
|
+
@value = val_.to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
def eql?(rhs_)
|
52
|
+
rhs_.is_a?(IndexWrapper) && @value == @rhs_.value
|
53
|
+
end
|
54
|
+
alias_method :==, :eql?
|
55
|
+
|
56
|
+
def hash
|
57
|
+
@value.hash
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :value
|
61
|
+
alias_method :to_i, :value
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Convenience method for creating an IndexWrapper
|
67
|
+
|
68
|
+
def self.index(val_)
|
69
|
+
IndexWrapper.new(val_)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
end
|
data/lib/ntable/structure.rb
CHANGED
@@ -132,6 +132,18 @@ module NTable
|
|
132
132
|
@axis_index -= 1
|
133
133
|
end
|
134
134
|
|
135
|
+
|
136
|
+
def _compute_offset(v_)
|
137
|
+
if v_.is_a?(::NTable::IndexWrapper)
|
138
|
+
index_ = v_.to_i
|
139
|
+
index_ = nil if index_ < 0 || index_ >= @axis_object.size
|
140
|
+
else
|
141
|
+
index_ = @axis_object.index(v_)
|
142
|
+
index_ = v_ if !index_ && v_.is_a?(::Integer) && v_ >= 0 && v_ < @axis_object.size
|
143
|
+
end
|
144
|
+
index_ ? @step * index_ : nil
|
145
|
+
end
|
146
|
+
|
135
147
|
end
|
136
148
|
|
137
149
|
|
@@ -551,9 +563,9 @@ module NTable
|
|
551
563
|
offset_ = 0
|
552
564
|
arg_.each do |k_, v_|
|
553
565
|
if (ainfo_ = axis(k_))
|
554
|
-
|
555
|
-
return nil unless
|
556
|
-
offset_ +=
|
566
|
+
delta_ = ainfo_._compute_offset(v_)
|
567
|
+
return nil unless delta_
|
568
|
+
offset_ += delta_
|
557
569
|
else
|
558
570
|
return nil
|
559
571
|
end
|
@@ -563,9 +575,9 @@ module NTable
|
|
563
575
|
offset_ = 0
|
564
576
|
arg_.each_with_index do |v_, i_|
|
565
577
|
if (ainfo_ = @indexes[i_])
|
566
|
-
|
567
|
-
return nil unless
|
568
|
-
offset_ +=
|
578
|
+
delta_ = ainfo_._compute_offset(v_)
|
579
|
+
return nil unless delta_
|
580
|
+
offset_ += delta_
|
569
581
|
else
|
570
582
|
return nil
|
571
583
|
end
|
data/lib/ntable/table.rb
CHANGED
@@ -189,9 +189,9 @@ module NTable
|
|
189
189
|
|
190
190
|
|
191
191
|
# Returns the value in the cell at the given coordinates, which
|
192
|
-
#
|
192
|
+
# may be given as labels or as 0-based row indexes.
|
193
193
|
# You may specify the cell as an array of coordinates, or as a
|
194
|
-
# hash mapping axis name to coordinate.
|
194
|
+
# hash mapping axis name or axis index to coordinate.
|
195
195
|
#
|
196
196
|
# For example, for a typical database result set with an axis called
|
197
197
|
# "row" of numerically identified rows, and an axis called "col" with
|
@@ -200,6 +200,23 @@ module NTable
|
|
200
200
|
# get(3, 'name')
|
201
201
|
# get([3, 'name'])
|
202
202
|
# get(:row => 3, :col => 'name')
|
203
|
+
# get(0 => 3, 1 => 'name')
|
204
|
+
#
|
205
|
+
# Alternately, you can provide row numbers (0-based) instead. If, for
|
206
|
+
# example, "name" is the second column (corresponding to index 1),
|
207
|
+
# then the following queries are also equivalent:
|
208
|
+
#
|
209
|
+
# get(3, 1)
|
210
|
+
# get(:row => 3, :col => 1)
|
211
|
+
#
|
212
|
+
# For axes whose labels are integers (for example, a numerically
|
213
|
+
# identified axis such as IndexedAxis), it is ambiguous whether a
|
214
|
+
# value is intended as a label or an index. In this case, NTable
|
215
|
+
# defalts to assuming the value is a label. If you want to force a
|
216
|
+
# value to be treated as a 0-based row index, wrap it in a call to
|
217
|
+
# NTable.index(), as follows:
|
218
|
+
#
|
219
|
+
# get(NTable.index(3), 1)
|
203
220
|
#
|
204
221
|
# Raises NoSuchCellError if the coordinates do not exist.
|
205
222
|
|
@@ -450,10 +467,10 @@ module NTable
|
|
450
467
|
# Performs a reduce on the entire table and returns the result.
|
451
468
|
# You may use one of the following call sequences:
|
452
469
|
#
|
453
|
-
# [
|
470
|
+
# [reduce_with_position{ |accumulator, value, position| <i>block</i> }]
|
454
471
|
# Reduces using the given block as the reduction function. The
|
455
472
|
# first element in the table is used as the initial accumulator.
|
456
|
-
# [
|
473
|
+
# [reduce_with_position(initial){ |accumulator, value, position| <i>block</i> }]
|
457
474
|
# Reduces using the given block as the reduction function, with
|
458
475
|
# the given initial value for the accumulator.
|
459
476
|
|
data/test/tc_basic_values.rb
CHANGED
@@ -47,7 +47,9 @@ module NTable
|
|
47
47
|
def setup
|
48
48
|
@labeled_axis = LabeledAxis.new([:red, :white, :blue])
|
49
49
|
@indexed_axis = IndexedAxis.new(10)
|
50
|
+
@indexed_axis_1 = IndexedAxis.new(10, 1)
|
50
51
|
@structure = Structure.add(@indexed_axis, :row).add(@labeled_axis, :column)
|
52
|
+
@structure_1 = Structure.add(@indexed_axis_1, :row).add(@labeled_axis, :column)
|
51
53
|
end
|
52
54
|
|
53
55
|
|
@@ -81,6 +83,15 @@ module NTable
|
|
81
83
|
end
|
82
84
|
|
83
85
|
|
86
|
+
def test_load_and_get_indexes
|
87
|
+
table_ = Table.new(@structure_1, :load => (0..29).to_a)
|
88
|
+
assert_equal(14, table_.get(5, 2))
|
89
|
+
assert_equal(14, table_.get(0 => 5, 1 => 2))
|
90
|
+
assert_equal(17, table_.get(::NTable.index(5), 2))
|
91
|
+
assert_equal(17, table_.get(0 => ::NTable.index(5), 1 => 2))
|
92
|
+
end
|
93
|
+
|
94
|
+
|
84
95
|
def test_set_from_array
|
85
96
|
table_ = Table.new(@structure)
|
86
97
|
table_.set!(0, :red, "foo")
|
@@ -101,6 +112,16 @@ module NTable
|
|
101
112
|
end
|
102
113
|
|
103
114
|
|
115
|
+
def test_set_indexes
|
116
|
+
table_ = Table.new(@structure_1)
|
117
|
+
table_.set!(1, 1, "foo")
|
118
|
+
table_[1 => 2, 0 => ::NTable.index(5)] = "bar"
|
119
|
+
assert_equal("foo", table_.get(::NTable.index(0), :white))
|
120
|
+
assert_equal("bar", table_[6, :blue])
|
121
|
+
assert_nil(table_.get(5, :blue))
|
122
|
+
end
|
123
|
+
|
124
|
+
|
104
125
|
def test_load_no_axes
|
105
126
|
t1_ = Table.new(Structure.new, :load => [1])
|
106
127
|
assert_equal(1, t1_.get)
|
data/test/tc_nested_object.rb
CHANGED
@@ -199,6 +199,22 @@ module NTable
|
|
199
199
|
end
|
200
200
|
|
201
201
|
|
202
|
+
def test_from_level_1_labeled_with_objectify_conversion
|
203
|
+
obj_ = {'one' => 1, 'two' => 2}
|
204
|
+
t1_ = Table.from_nested_object(obj_,
|
205
|
+
[{:sort => true, :objectify => ::Proc.new{ |a_| a_.to_sym }}])
|
206
|
+
assert_equal(Table.new(Structure.add(@object_axis_2), :load => [1,2]), t1_)
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
def test_from_level_1_labeled_with_stringify_conversion
|
211
|
+
obj_ = {:one1 => 1, :two22 => 2}
|
212
|
+
t1_ = Table.from_nested_object(obj_,
|
213
|
+
[{:sort => true, :stringify => ::Proc.new{ |a_| a_.to_s.gsub(/\d/, '') }}])
|
214
|
+
assert_equal(Table.new(Structure.add(@labeled_axis_2), :load => [1,2]), t1_)
|
215
|
+
end
|
216
|
+
|
217
|
+
|
202
218
|
end
|
203
219
|
|
204
220
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ntable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- lib/ntable/axis.rb
|
29
29
|
- lib/ntable/construction.rb
|
30
30
|
- lib/ntable/errors.rb
|
31
|
+
- lib/ntable/index_wrapper.rb
|
31
32
|
- lib/ntable/structure.rb
|
32
33
|
- lib/ntable/table.rb
|
33
34
|
- lib/ntable.rb
|