fat_table 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa339300269582eedb57d26138bd3fa2de68d526ed4cc94e4c3a369a67989ddb
4
- data.tar.gz: b35cc4785b208b670d6f039de8ef12351ea0533c37d8802fbf89f57763e63330
3
+ metadata.gz: 0ecb8cb3fb29acb95b0fe11e65c5b587379a5a3070ab68acbc6a2c1785c8f229
4
+ data.tar.gz: f3cc9e7242ded9fd0970159eee8da4208faade0e6e4c37a324a45e1596c4eeae
5
5
  SHA512:
6
- metadata.gz: 49fbab273c609035f9c2a179b0b97cdca067a8a30db754916e7b17d03a435b4d3a4eefb89ddb326ef07c6e5b91632f764ce101de0acbc79fcf6bd876741da4cf
7
- data.tar.gz: 76f87e5b342da743ea3e0c2000273de35d081ad4835c2423d6342278ebbced67d3a34bd11526c7f32185ac19e0b225356baa02fbcb8b7b630868739a944f0ecb
6
+ metadata.gz: 8d404bd69ba3a1c3c9774f634e4f0935987ff4827803d59e89b9adbdc4806b25ef87e5dbcb9f22d7e3475c86dceebf097cf73e0ad1287f91d21415e5b0a5a3b3
7
+ data.tar.gz: 8d8df3651b9cdcdeaefa0527cd4fc1987c1ec491bca2a88651b255032eea16ee29c82285b9d7f9bae2ecb9b6d90c6fc4d766980af5362dc89045d553e66dec03
data/README.org CHANGED
@@ -638,8 +638,11 @@ something that can't be parsed as a Numeric. ~FatTable~ raises an exception
638
638
  is such cases, and that may be what you want if you can control the input.
639
639
  But, especially when you cannot do so, it can be helpful to designate one or
640
640
  more columns as "tolerant." This means that when a conversion problem occurs,
641
- the column is forced to String type instead of throwing an exception, and the
642
- table can continue to be read.
641
+ the column item is retained as a string type in a column that is otherwise of
642
+ one of the types Numeric, DateTime, or Boolean. Those string items are
643
+ treated as nils for purposes of sorting or evaluation in a ~select~ method.
644
+ When formatted, they participate in string formatting directive, but not those
645
+ for other types.
643
646
 
644
647
  All of the table construction methods, allow a keyword parameter,
645
648
  ~tolerant_columns~, where you can designate what columns should be convert to
@@ -2641,9 +2644,10 @@ the table, but they can be overridden by more specific directives given in a
2641
2644
  ~format_for~ directive.
2642
2645
 
2643
2646
  **** Type and Column priority
2644
- A directive based on type applies to all columns having that type unless
2645
- overridden by a directive specific to a named column; a directive based on a
2646
- column name applies only to cells in that column.
2647
+ A directive based the column name overrides any directive based on type. If
2648
+ any cell has both a type-based formatting and column-based, the column
2649
+ instructions prevail. In earlier versions the instuctions were "merged" but
2650
+ that is no longer the case.
2647
2651
 
2648
2652
  However, there is a twist. Since the end result of formatting is to convert
2649
2653
  all columns to strings, the formatting directives for the ~String~ type can
data/lib/ext/array.rb CHANGED
@@ -12,4 +12,21 @@ class Array
12
12
  end
13
13
  end
14
14
  end
15
+
16
+ def filter_to_type(typ)
17
+ if typ == 'Boolean'
18
+ compact.select { |i| i.is_a?(TrueClass) || i.is_a?(FalseClass) }
19
+ elsif typ == 'DateTime'
20
+ compact.select { |i| i.is_a?(Date) || i.is_a?(DateTime) || i.is_a?(Time) }
21
+ .map { |i| i.to_datetime }
22
+ elsif typ == 'Numeric'
23
+ compact.select { |i| i.is_a?(Numeric) }
24
+ elsif typ == 'String'
25
+ map { |i| i.to_s }
26
+ elsif typ == 'NilClass'
27
+ self
28
+ else
29
+ raise ArgumentError, "cannot filter_to_type for type '#{typ}'"
30
+ end
31
+ end
15
32
  end
@@ -196,7 +196,7 @@ module FatTable
196
196
  if type == 'String'
197
197
  items.reject(&:blank?).first
198
198
  else
199
- items.compact.first
199
+ items.filter_to_type(type).first
200
200
  end
201
201
  end
202
202
 
@@ -207,7 +207,7 @@ module FatTable
207
207
  if type == 'String'
208
208
  items.reject(&:blank?).last
209
209
  else
210
- items.compact.last
210
+ items.filter_to_type(type).last
211
211
  end
212
212
  end
213
213
 
@@ -219,7 +219,7 @@ module FatTable
219
219
  if type == 'String'
220
220
  items.reject(&:blank?).count.to_d
221
221
  else
222
- items.compact.count.to_d
222
+ items.filter_to_type(type).count.to_d
223
223
  end
224
224
  end
225
225
 
@@ -232,7 +232,7 @@ module FatTable
232
232
  if type == 'String'
233
233
  items.reject(&:blank?).min
234
234
  else
235
- items.compact.min
235
+ items.filter_to_type(type).min
236
236
  end
237
237
  end
238
238
 
@@ -245,7 +245,7 @@ module FatTable
245
245
  if type == 'String'
246
246
  items.reject(&:blank?).max
247
247
  else
248
- items.compact.max
248
+ items.filter_to_type(type).max
249
249
  end
250
250
  end
251
251
 
@@ -268,7 +268,7 @@ module FatTable
268
268
  if type == 'String'
269
269
  items.reject(&:blank?).join(' ')
270
270
  else
271
- items.compact.sum
271
+ items.filter_to_type(type).sum
272
272
  end
273
273
  end
274
274
 
@@ -280,7 +280,7 @@ module FatTable
280
280
  # average back to a DateTime.
281
281
  def avg
282
282
  only_with('avg', 'DateTime', 'Numeric')
283
- itms = items.compact
283
+ itms = items.filter_to_type(type)
284
284
  size = itms.size.to_d
285
285
  if type == 'DateTime'
286
286
  avg_jd = itms.map(&:jd).sum / size
@@ -301,9 +301,9 @@ module FatTable
301
301
  only_with('var', 'DateTime', 'Numeric')
302
302
  all_items =
303
303
  if type == 'DateTime'
304
- items.compact.map(&:jd)
304
+ items.filter_to_type(type).map(&:jd)
305
305
  else
306
- items.compact
306
+ items.filter_to_type(type)
307
307
  end
308
308
  n = count
309
309
  return BigDecimal('0.0') if n <= 1
@@ -324,7 +324,7 @@ module FatTable
324
324
  # number and computes the variance of those numbers.
325
325
  def pvar
326
326
  only_with('var', 'DateTime', 'Numeric')
327
- n = items.compact.size.to_d
327
+ n = items.filter_to_type(type).size.to_d
328
328
  return BigDecimal('0.0') if n <= 1
329
329
  var * ((n - 1) / n)
330
330
  end
@@ -361,7 +361,7 @@ module FatTable
361
361
  # false. Works only with boolean Columns.
362
362
  def any?
363
363
  only_with('any?', 'Boolean')
364
- items.compact.any?
364
+ items.filter_to_type(type).any?
365
365
  end
366
366
 
367
367
  # :category: Aggregates
@@ -370,7 +370,7 @@ module FatTable
370
370
  # false. Works only with boolean Columns.
371
371
  def all?
372
372
  only_with('all?', 'Boolean')
373
- items.compact.all?
373
+ items.filter_to_type(type).all?
374
374
  end
375
375
 
376
376
  # :category: Aggregates
@@ -379,7 +379,7 @@ module FatTable
379
379
  # false. Works only with boolean Columns.
380
380
  def none?
381
381
  only_with('none?', 'Boolean')
382
- items.compact.none?
382
+ items.filter_to_type(type).none?
383
383
  end
384
384
 
385
385
  # :category: Aggregates
@@ -388,7 +388,7 @@ module FatTable
388
388
  # otherwise return false. Works only with boolean Columns.
389
389
  def one?
390
390
  only_with('one?', 'Boolean')
391
- items.compact.one?
391
+ items.filter_to_type(type).one?
392
392
  end
393
393
 
394
394
  private
@@ -413,11 +413,10 @@ module FatTable
413
413
  # a tolerant column, respond to type errors by converting the column to a
414
414
  # String type.
415
415
  def <<(itm)
416
- items << convert_to_type(itm)
416
+ items << convert_and_set_type(itm)
417
417
  rescue IncompatibleTypeError => ex
418
418
  if tolerant?
419
- force_string!
420
- retry
419
+ items << Convert.convert_to_string(itm)
421
420
  else
422
421
  raise ex
423
422
  end
@@ -436,9 +435,9 @@ module FatTable
436
435
 
437
436
  private
438
437
 
439
- def convert_to_type(val)
440
- new_val = Convert.convert_to_type(val, type)
441
- if new_val && type == 'NilClass'
438
+ def convert_and_set_type(val)
439
+ new_val = Convert.convert_to_type(val, type, tolerant: tolerant?)
440
+ if new_val && (type == 'NilClass' || type == 'String')
442
441
  @type =
443
442
  if [true, false].include?(new_val)
444
443
  'Boolean'
@@ -10,7 +10,7 @@ module FatTable
10
10
  # determined, raise an error if the val cannot be converted to the Column
11
11
  # type. Otherwise, returns the converted val as an object of the correct
12
12
  # class.
13
- def self.convert_to_type(val, type)
13
+ def self.convert_to_type(val, type, tolerant: false)
14
14
  case type
15
15
  when 'NilClass'
16
16
  if val != false && val.blank?
@@ -66,6 +66,19 @@ module FatTable
66
66
  when 'String'
67
67
  if val.nil?
68
68
  nil
69
+ elsif tolerant
70
+ # Allow String to upgrade to one of Numeric, DateTime, or Boolean if
71
+ # possible.
72
+ if (new_val = convert_to_numeric(val))
73
+ new_val
74
+ elsif (new_val = convert_to_date_time(val))
75
+ new_val
76
+ elsif (new_val = convert_to_boolean(val))
77
+ new_val
78
+ else
79
+ new_val = convert_to_string(val)
80
+ end
81
+ new_val
69
82
  else
70
83
  new_val = convert_to_string(val)
71
84
  if new_val.nil?
@@ -45,17 +45,15 @@ module FatTable
45
45
  end
46
46
 
47
47
  # Return the result of evaluating +expr+ as a Ruby expression in which the
48
- # instance variables set in Evaluator.new and any local variables set in the
49
- # Hash parameter +locals+ are available to the expression.
48
+ # instance variables set in Evaluator.new and any local variables set in
49
+ # the Hash parameter +locals+ are available to the expression. Certain
50
+ # errors simply return nil as the result. This can happen, for example,
51
+ # when a string gets into an otherwise numeric column because the column
52
+ # is set to tolerant.
50
53
  def evaluate(expr = '', locals: {})
51
54
  eval(expr, local_vars(binding, locals))
52
55
  rescue NoMethodError, TypeError => ex
53
- if ex.to_s =~ /for nil:NilClass|nil can't be coerced/
54
- # Likely one of the locals was nil, so let nil be the result.
55
- return nil
56
- else
57
- raise ex
58
- end
56
+ nil
59
57
  end
60
58
 
61
59
  private
@@ -562,20 +562,21 @@ module FatTable
562
562
  end
563
563
  end
564
564
 
565
- # Merge in formatting for column h based on the column type, or based
566
- # on the string type for the header location.
565
+ # Merge in formatting instructions for column h based on the column
566
+ # name, or if there is no formatting instructions for the column by
567
+ # name, merge in the formatting instructions based on the column's
568
+ # type. Insist on only the string type for the header location.
567
569
  typ = (location == :header ? :string : table.type(h).as_sym)
568
570
  parse_typ_method_name = 'parse_' + typ.to_s + '_fmt'
569
- if fmts.key?(typ)
570
- # Merge in type-based formatting
571
- typ_fmt = send(parse_typ_method_name, fmts[typ]).first
572
- format_h = format_h.merge(typ_fmt)
573
- end
574
571
  if fmts[h]
575
572
  # Merge in column formatting
576
573
  col_fmt = send(parse_typ_method_name, fmts[h],
577
574
  strict: location != :header).first
578
575
  format_h = format_h.merge(col_fmt)
576
+ elsif fmts.key?(typ)
577
+ # Merge in type-based formatting
578
+ typ_fmt = send(parse_typ_method_name, fmts[typ]).first
579
+ format_h = format_h.merge(typ_fmt)
579
580
  end
580
581
 
581
582
  # Copy :body formatting for column h to :bfirst and :gfirst if they
@@ -779,7 +779,6 @@ module FatTable
779
779
  last_key = nil
780
780
  new_rows.each_with_index do |nrow, k|
781
781
  new_tab << nrow
782
- # key = nrow.fetch_values(*sort_heads)
783
782
  key = nrow.fetch_values(*key_hash.keys)
784
783
  new_tab.mark_boundary(k - 1) if last_key && key != last_key
785
784
  last_key = key
@@ -1712,11 +1711,19 @@ module FatTable
1712
1711
  end
1713
1712
 
1714
1713
  # The <=> operator cannot handle nils without some help. Treat a nil as
1715
- # smaller than any other value, but equal to other nils. The two keys are assumed to be arrays of values to be
1716
- # compared with <=>.
1714
+ # smaller than any other value, but equal to other nils. The two keys are
1715
+ # assumed to be arrays of values to be compared with <=>. Since
1716
+ # tolerant_columns permit strings to be mixed in with columns of type
1717
+ # Numeric, DateTime, and Boolean, treat strings mixed with another type
1718
+ # the same as nils.
1717
1719
  def compare_with_nils(key1, key2)
1718
1720
  result = nil
1719
1721
  key1.zip(key2) do |k1, k2|
1722
+ if k1.is_a?(String) && !k2.is_a?(String)
1723
+ k1 = nil
1724
+ elsif !k1.is_a?(String) && k2.is_a?(String)
1725
+ k2 = nil
1726
+ end
1720
1727
  if k1.nil? && k2.nil?
1721
1728
  result = 0
1722
1729
  next
@@ -2,5 +2,5 @@
2
2
 
3
3
  module FatTable
4
4
  # The current version of FatTable
5
- VERSION = '0.5.3'
5
+ VERSION = '0.5.4'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fat_table
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel E. Doherty
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-24 00:00:00.000000000 Z
11
+ date: 2022-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler