ntable 0.1.3 → 0.1.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.
@@ -1,3 +1,8 @@
1
+ === 0.1.4 / 2012-09-24
2
+
3
+ * NTable::Structure and NTable::AxisInfo are now Enumerable, letting you iterate over the axes and labels, respectively.
4
+ * You can now postprocess the labels generated by NTable.from_nested_object. Useful for filling in labels missing in the data.
5
+
1
6
  === 0.1.3 / 2012-09-10
2
7
 
3
8
  * Creating a table from a nested object, using a label conversion procedure, tended to fail with NoSuchCellError. Fixed and added test cases.
data/Version CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -131,40 +131,45 @@ module NTable
131
131
  end
132
132
 
133
133
 
134
+ # See EmptyAxis#eql?
134
135
  def eql?(obj_)
135
136
  obj_.is_a?(LabeledAxis) && @a.eql?(obj_.instance_variable_get(:@a))
136
137
  end
138
+ alias_method :==, :eql?
137
139
 
138
- def ==(obj_)
139
- obj_.is_a?(LabeledAxis) && @a == obj_.instance_variable_get(:@a)
140
- end
141
-
140
+ # See EmptyAxis#hash
142
141
  def hash
143
142
  @a.hash
144
143
  end
145
144
 
145
+ # See EmptyAxis#inspect
146
146
  def inspect
147
147
  "#<#{self.class}:0x#{object_id.to_s(16)} #{@a.inspect}>"
148
148
  end
149
149
  alias_method :to_s, :inspect
150
150
 
151
151
 
152
+ # See EmptyAxis#size
152
153
  attr_reader :size
153
154
 
154
155
 
156
+ # See EmptyAxis#index
155
157
  def index(label_)
156
158
  @h[label_.to_s]
157
159
  end
158
160
 
161
+ # See EmptyAxis#label
159
162
  def label(index_)
160
163
  @a[index_]
161
164
  end
162
165
 
163
166
 
167
+ # See EmptyAxis#to_json_object
164
168
  def to_json_object(json_obj_)
165
169
  json_obj_['labels'] = @a
166
170
  end
167
171
 
172
+ # See EmptyAxis#from_json_object
168
173
  def from_json_object(json_obj_)
169
174
  initialize(json_obj_['labels'] || [])
170
175
  end
@@ -188,39 +193,49 @@ module NTable
188
193
  end
189
194
 
190
195
 
196
+ # See EmptyAxis#eql?
191
197
  def eql?(obj_)
192
198
  obj_.is_a?(IndexedAxis) && obj_.size.eql?(@size) && obj_.start.eql?(@start)
193
199
  end
194
200
  alias_method :==, :eql?
195
201
 
202
+ # See EmptyAxis#hash
196
203
  def hash
197
204
  @size.hash ^ @start.hash
198
205
  end
199
206
 
207
+ # See EmptyAxis#inspect
200
208
  def inspect
201
209
  "#<#{self.class}:0x#{object_id.to_s(16)} size=#{@size} start=#{@start}>"
202
210
  end
203
211
  alias_method :to_s, :inspect
204
212
 
205
213
 
214
+ # See EmptyAxis#size
206
215
  attr_reader :size
216
+
217
+ # Retrieve the number of the first row
207
218
  attr_reader :start
208
219
 
209
220
 
221
+ # See EmptyAxis#index
210
222
  def index(label_)
211
223
  label_ >= @start && label_ < @size + @start ? label_ - @start : nil
212
224
  end
213
225
 
226
+ # See EmptyAxis#label
214
227
  def label(index_)
215
228
  index_ >= 0 && index_ < @size ? index_ + @start : nil
216
229
  end
217
230
 
218
231
 
232
+ # See EmptyAxis#to_json_object
219
233
  def to_json_object(json_obj_)
220
234
  json_obj_['size'] = @size
221
235
  json_obj_['start'] = @start unless @start == 0
222
236
  end
223
237
 
238
+ # See EmptyAxis#from_json_object
224
239
  def from_json_object(json_obj_)
225
240
  initialize(json_obj_['size'], json_obj_['start'].to_i)
226
241
  end
@@ -245,40 +260,49 @@ module NTable
245
260
  end
246
261
 
247
262
 
263
+ # See EmptyAxis#eql?
248
264
  def eql?(obj_)
249
265
  obj_.is_a?(ObjectAxis) && @a.eql?(obj_.instance_variable_get(:@a))
250
266
  end
251
267
 
268
+ # See EmptyAxis#==
252
269
  def ==(obj_)
253
270
  obj_.is_a?(ObjectAxis) && @a == obj_.instance_variable_get(:@a)
254
271
  end
255
272
 
273
+ # See EmptyAxis#hash
256
274
  def hash
257
275
  @a.hash
258
276
  end
259
277
 
278
+ # See EmptyAxis#inspect
260
279
  def inspect
261
280
  "#<#{self.class}:0x#{object_id.to_s(16)} #{@a.inspect}>"
262
281
  end
263
282
  alias_method :to_s, :inspect
264
283
 
265
284
 
285
+ # See EmptyAxis#size
266
286
  attr_reader :size
267
287
 
268
288
 
289
+ # See EmptyAxis#index
269
290
  def index(label_)
270
291
  @h[label_]
271
292
  end
272
293
 
294
+ # See EmptyAxis#label
273
295
  def label(index_)
274
296
  @a[index_]
275
297
  end
276
298
 
277
299
 
300
+ # See EmptyAxis#to_json_object
278
301
  def to_json_object(json_obj_)
279
302
  raise "Unable to JSON serialize an ObjectAxis"
280
303
  end
281
304
 
305
+ # See EmptyAxis#from_json_object
282
306
  def from_json_object(json_obj_)
283
307
  raise "Unable to JSON serialize an ObjectAxis"
284
308
  end
@@ -41,9 +41,9 @@ require 'json'
41
41
  module NTable
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
+ @numeric_sort = ->(a_, b_){ a_.to_f <=> b_.to_f }
45
+ @integer_sort = ->(a_, b_){ a_.to_i <=> b_.to_i }
46
+ @string_sort = ->(a_, b_){ a_.to_s <=> b_.to_s }
47
47
 
48
48
 
49
49
  class << self
@@ -119,6 +119,13 @@ module NTable
119
119
  # proc is provided, the resulting axis will be a LabeledAxis.
120
120
  # You can also pass true instead of a Proc; this will create an
121
121
  # LabeledAxis and make the conversion a simple to_s.
122
+ # [<tt>:postprocess</tt>]
123
+ # An optional Proc that postprocesses the final labels array.
124
+ # It should take an array of labels and return a modified array
125
+ # (which can be the original array modified in place). Called
126
+ # after any sort has been completed.
127
+ # You can use this, for example, to "fill in" labels that were
128
+ # not present in the original data.
122
129
  #
123
130
  # The third argument is an optional hash of miscellaneous options.
124
131
  # The following keys are recognized:
@@ -188,6 +195,8 @@ module NTable
188
195
  end
189
196
  labels_.sort!(&func_)
190
197
  end
198
+ postprocess_ = field_[:postprocess]
199
+ labels_ = postprocess_.call(labels_) if postprocess_.respond_to?(:call)
191
200
  axis_ = klass_.new(labels_)
192
201
  when ::Array
193
202
  axis_ = IndexedAxis.new(ai_[1].to_i - ai_[0].to_i, ai_[0].to_i)
@@ -42,24 +42,35 @@ module NTable
42
42
 
43
43
  class IndexWrapper
44
44
 
45
+
45
46
  # Create an IndexWrapper with the given integer index
46
47
 
47
48
  def initialize(val_)
48
49
  @value = val_.to_i
49
50
  end
50
51
 
52
+
53
+ # Standard equality checker
54
+
51
55
  def eql?(rhs_)
52
56
  rhs_.is_a?(IndexWrapper) && @value == @rhs_.value
53
57
  end
54
58
  alias_method :==, :eql?
55
59
 
60
+
61
+ # Standard hash value
62
+
56
63
  def hash
57
64
  @value.hash
58
65
  end
59
66
 
67
+
68
+ # Retrieve the actual index
69
+
60
70
  attr_reader :value
61
71
  alias_method :to_i, :value
62
72
 
73
+
63
74
  end
64
75
 
65
76
 
@@ -104,6 +104,7 @@ module NTable
104
104
  def label(index_)
105
105
  @axis_object.label(index_)
106
106
  end
107
+ alias_method :[], :label
107
108
 
108
109
 
109
110
  # Return the number of rows along this axis.
@@ -114,10 +115,27 @@ module NTable
114
115
  end
115
116
 
116
117
 
117
- def eql?(obj_) # :nodoc:
118
+ # Iterate over the labels, in order.
119
+
120
+ def each
121
+ if block_given?
122
+ @axis_object.size.times do |i_|
123
+ yield @axis_object.label(i_)
124
+ end
125
+ else
126
+ to_enum
127
+ end
128
+ end
129
+
130
+ include ::Enumerable
131
+
132
+
133
+ # Standard equality check
134
+
135
+ def eql?(obj_)
118
136
  obj_.is_a?(AxisInfo) && @axis_object.eql?(obj_.axis_object) && @axis_name.eql?(obj_.axis_name)
119
137
  end
120
- alias_method :==, :eql? # :nodoc:
138
+ alias_method :==, :eql?
121
139
 
122
140
 
123
141
  def _set_axis(axis_) # :nodoc:
@@ -133,7 +151,7 @@ module NTable
133
151
  end
134
152
 
135
153
 
136
- def _compute_offset(v_)
154
+ def _compute_offset(v_) # :nodoc:
137
155
  if v_.is_a?(::NTable::IndexWrapper)
138
156
  index_ = v_.to_i
139
157
  index_ = nil if index_ < 0 || index_ >= @axis_object.size
@@ -160,11 +178,21 @@ module NTable
160
178
  end
161
179
 
162
180
 
181
+ # Standard equality check
182
+
163
183
  def eql?(obj_)
164
184
  obj_.is_a?(Position) && obj_.structure.eql?(@structure) && obj_._offset.eql?(self._offset)
165
185
  end
166
186
  alias_method :==, :eql?
167
187
 
188
+
189
+ # Standard hash value
190
+
191
+ def hash
192
+ @structure.hash + @vector.hash
193
+ end
194
+
195
+
168
196
  attr_reader :structure # :nodoc:
169
197
 
170
198
 
@@ -398,11 +426,21 @@ module NTable
398
426
  @names[axis_.to_s]
399
427
  end
400
428
  end
429
+ alias_method :[], :axis
430
+
431
+
432
+ # Iterate over the axes in order, yielding AxisInfo objects.
433
+
434
+ def each(&block_)
435
+ @indexes.each(&block_)
436
+ end
437
+
438
+ include ::Enumerable
401
439
 
402
440
 
403
441
  # Lock this structure, preventing further modification. Generally,
404
- # this is done automatically when a structure is used by a table,
405
- # and you do not need to call it yourself.
442
+ # this is done automatically when a structure is used by a table;
443
+ # you normally do not need to call it yourself.
406
444
 
407
445
  def lock!
408
446
  unless @locked
@@ -128,6 +128,13 @@ module NTable
128
128
  end
129
129
 
130
130
 
131
+ # Standard hash value
132
+
133
+ def hash
134
+ @structure.hash + @vals.hash + @offset.hash + @parent.hash
135
+ end
136
+
137
+
131
138
  # The Structure of this table
132
139
  attr_reader :structure
133
140
 
@@ -643,7 +650,7 @@ module NTable
643
650
  end
644
651
 
645
652
 
646
- def _offset_for_args(args_)
653
+ def _offset_for_args(args_) # :nodoc:
647
654
  if args_.size == 1
648
655
  first_ = args_.first
649
656
  args_ = first_ if first_.is_a?(::Hash) || first_.is_a?(::Array)
@@ -47,6 +47,7 @@ module NTable
47
47
  def setup
48
48
  @labeled_axis_2 = LabeledAxis.new([:one, :two])
49
49
  @object_axis_2 = ObjectAxis.new([:one, :two])
50
+ @object_axis_3 = ObjectAxis.new([:one, :two, :three])
50
51
  @labeled_axis_3 = LabeledAxis.new([:blue, :red, :white])
51
52
  @indexed_axis_2 = IndexedAxis.new(2)
52
53
  @indexed_axis_10 = IndexedAxis.new(10, 1)
@@ -202,7 +203,7 @@ module NTable
202
203
  def test_from_level_1_labeled_with_objectify_conversion
203
204
  obj_ = {'one' => 1, 'two' => 2}
204
205
  t1_ = Table.from_nested_object(obj_,
205
- [{:sort => true, :objectify => ::Proc.new{ |a_| a_.to_sym }}])
206
+ [{:sort => true, :objectify => ->(a_){ a_.to_sym }}])
206
207
  assert_equal(Table.new(Structure.add(@object_axis_2), :load => [1,2]), t1_)
207
208
  end
208
209
 
@@ -210,11 +211,20 @@ module NTable
210
211
  def test_from_level_1_labeled_with_stringify_conversion
211
212
  obj_ = {:one1 => 1, :two22 => 2}
212
213
  t1_ = Table.from_nested_object(obj_,
213
- [{:sort => true, :stringify => ::Proc.new{ |a_| a_.to_s.gsub(/\d/, '') }}])
214
+ [{:sort => true, :stringify => ->(a_){ a_.to_s.gsub(/\d/, '') }}])
214
215
  assert_equal(Table.new(Structure.add(@labeled_axis_2), :load => [1,2]), t1_)
215
216
  end
216
217
 
217
218
 
219
+ def test_from_level_1_labeled_with_objectify_and_postprocess
220
+ obj_ = {:one => 1, :two => 2}
221
+ t1_ = Table.from_nested_object(obj_,
222
+ [{:sort => true, :objectify => true, :postprocess => ->(labels_){ labels_ << :three }}],
223
+ :fill => 0)
224
+ assert_equal(Table.new(Structure.add(@object_axis_3), :load => [1,2,0]), t1_)
225
+ end
226
+
227
+
218
228
  end
219
229
 
220
230
  end
@@ -74,6 +74,13 @@ module NTable
74
74
  end
75
75
 
76
76
 
77
+ def test_axis_info_enumerable
78
+ s_ = Structure.new
79
+ s_.add(@indexed1, :first)
80
+ assert_equal(45, s_.axis(0).inject(:+))
81
+ end
82
+
83
+
77
84
  def test_add_multi_axis
78
85
  s_ = Structure.new
79
86
  s_.add(@labeled1, :first)
@@ -92,6 +99,15 @@ module NTable
92
99
  end
93
100
 
94
101
 
102
+ def test_structure_enumerable
103
+ s_ = Structure.new
104
+ s_.add(@labeled1, :first)
105
+ s_.add(@indexed1, :second)
106
+ s_.add(@indexed1)
107
+ assert_equal([LabeledAxis, IndexedAxis, IndexedAxis], s_.map{ |ai_| ai_.axis_object.class })
108
+ end
109
+
110
+
95
111
  def test_remove_axis
96
112
  s_ = Structure.new
97
113
  s_.add(@labeled1, :first)
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.3
4
+ version: 0.1.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-09-10 00:00:00.000000000 Z
12
+ date: 2012-09-24 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: NTable provides a convenient data structure for storing n-dimensional
15
15
  tabular data. It works with zero-dimensional scalar values, arrays, tables, and
@@ -64,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
64
  version: 1.3.1
65
65
  requirements: []
66
66
  rubyforge_project: virtuoso
67
- rubygems_version: 1.8.21
67
+ rubygems_version: 1.8.24
68
68
  signing_key:
69
69
  specification_version: 3
70
70
  summary: NTable is an n-dimensional table data structure for Ruby.