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 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.2
1
+ 0.1.3
data/lib/ntable.rb CHANGED
@@ -99,5 +99,6 @@ end
99
99
  require 'ntable/errors'
100
100
  require 'ntable/axis'
101
101
  require 'ntable/structure'
102
+ require 'ntable/index_wrapper'
102
103
  require 'ntable/table'
103
104
  require 'ntable/construction'
@@ -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
- labels_ = ai_.keys
152
- labels_.map!(&objectify_) if objectify_.respond_to?(:call)
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
- ai_.each do |k_, v_|
158
- k_ = stringify_.call(k_) if stringify_
159
- h_[k_.to_s] = true
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_.keys
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_] = true } if ::Array === ai_
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_] = true
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_] = true
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
@@ -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
- index_ = ainfo_.index(v_)
555
- return nil unless index_
556
- offset_ += ainfo_.step * index_
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
- index_ = ainfo_.index(v_)
567
- return nil unless index_
568
- offset_ += ainfo_.step * index_
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
- # must be given as labels.
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
- # [reduce{ |accumulator, value, position| <i>block</i> }]
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
- # [reduce(initial){ |accumulator, value, position| <i>block</i> }]
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
 
@@ -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)
@@ -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.2
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