ntable 0.1.2 → 0.1.3

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/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