fat_core 6.0.0 → 7.0.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +54 -0
- data/.rubocop.yml +4 -2
- data/.simplecov +2 -0
- data/CHANGELOG.org +49 -0
- data/Gemfile +5 -4
- data/README.org +1018 -58
- data/Rakefile +15 -2
- data/bin/console +1 -0
- data/fat_core.gemspec +1 -0
- data/lib/fat_core/all.rb +9 -10
- data/lib/fat_core/array.rb +5 -5
- data/lib/fat_core/enumerable.rb +0 -15
- data/lib/fat_core/hash.rb +48 -11
- data/lib/fat_core/numeric.rb +48 -1
- data/lib/fat_core/range.rb +108 -55
- data/lib/fat_core/string.rb +70 -37
- data/lib/fat_core/symbol.rb +1 -1
- data/lib/fat_core/version.rb +2 -2
- data/lib/fat_core.rb +2 -0
- data/spec/lib/array_spec.rb +2 -0
- data/spec/lib/big_decimal_spec.rb +2 -0
- data/spec/lib/enumerable_spec.rb +29 -39
- data/spec/lib/hash_spec.rb +19 -2
- data/spec/lib/nil_class_spec.rb +2 -0
- data/spec/lib/numeric_spec.rb +14 -0
- data/spec/lib/range_spec.rb +10 -0
- data/spec/lib/string_spec.rb +78 -11
- data/spec/lib/symbol_spec.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- metadata +8 -5
- data/lib/fat_core/kernel.rb +0 -26
- data/spec/lib/kernel_spec.rb +0 -13
data/README.org
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
#+PROPERTY: header-args:ruby :colnames no :hlines yes :exports both :wrap example :ruby ruby
|
|
4
4
|
#+PROPERTY: header-args:sh :exports code
|
|
5
5
|
|
|
6
|
-
[[https://
|
|
6
|
+
[[https://github.com/ddoherty03/fat_core/actions/workflows/ruby.yml][https://github.com/ddoherty03/fat_core/actions/workflows/ruby.yml/badge.svg?branch=master]]
|
|
7
7
|
|
|
8
|
-
* README Setup Do First for Code Blocks
|
|
8
|
+
* README Setup Do First for Code Blocks :noexport:
|
|
9
9
|
Run this block before all others to ensure that we are reading the libraries
|
|
10
10
|
from the source directory.
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ from the source directory.
|
|
|
15
15
|
$:.unshift("./lib") unless $:[0] == './lib'
|
|
16
16
|
$:[0..10].each { |d| puts d }
|
|
17
17
|
puts "..."
|
|
18
|
-
|
|
18
|
+
require_relative 'lib/fat_core/all' # => true
|
|
19
19
|
#+end_src
|
|
20
20
|
|
|
21
21
|
#+RESULTS:
|
|
@@ -24,29 +24,85 @@ Current directory: /home/ded/src/fat_core
|
|
|
24
24
|
Ruby LOADPATH:
|
|
25
25
|
./lib
|
|
26
26
|
/home/ded/.rbenv/rbenv.d/exec/gem-rehash
|
|
27
|
-
/home/ded/.rbenv/versions/3.
|
|
28
|
-
/home/ded/.rbenv/versions/3.
|
|
29
|
-
/home/ded/.rbenv/versions/3.
|
|
30
|
-
/home/ded/.rbenv/versions/3.
|
|
31
|
-
/home/ded/.rbenv/versions/3.
|
|
32
|
-
/home/ded/.rbenv/versions/3.
|
|
33
|
-
/home/ded/.rbenv/versions/3.
|
|
34
|
-
/home/ded/.rbenv/versions/3.
|
|
27
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
|
|
28
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-linux
|
|
29
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/site_ruby
|
|
30
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
|
|
31
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-linux
|
|
32
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/vendor_ruby
|
|
33
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/3.2.0
|
|
34
|
+
/home/ded/.rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-linux
|
|
35
35
|
...
|
|
36
36
|
#+end_example
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
* Table of Contents :toc:noexport:
|
|
40
|
+
- [[#version][Version]]
|
|
41
|
+
- [[#fatcore][FatCore]]
|
|
42
|
+
- [[#installation][Installation]]
|
|
43
|
+
- [[#usage][Usage]]
|
|
44
|
+
- [[#array][Array]]
|
|
45
|
+
- [[#method-comma_joinsep-nil-last_sep-nil-two_sep-nil][Method ~#comma_join(sep: nil, last_sep: nil, two_sep: nil)~]]
|
|
46
|
+
- [[#method-last_i][Method ~#last_i~]]
|
|
47
|
+
- [[#method-intersect_with_dups][Method ~#intersect_with_dups~]]
|
|
48
|
+
- [[#method-diff_with_dups][Method ~diff_with_dups~]]
|
|
49
|
+
- [[#bigdecimal-inspect][BigDecimal ~#inspect~]]
|
|
50
|
+
- [[#enumerable][Enumerable]]
|
|
51
|
+
- [[#method-each_with_flags][Method ~#each_with_flags~]]
|
|
52
|
+
- [[#hash][Hash]]
|
|
53
|
+
- [[#method-each_pair_with_flags][Method ~#each_pair_with_flags~]]
|
|
54
|
+
- [[#method-delete_with_value-and-delete_with_value][Method ~#delete_with_value~ and ~#delete_with_value!~]]
|
|
55
|
+
- [[#method-keys_with_value][Method ~#keys_with_value~]]
|
|
56
|
+
- [[#method-remap_keys][Method ~#remap_keys~]]
|
|
57
|
+
- [[#method-replace_keys][Method ~#replace_keys~]]
|
|
58
|
+
- [[#alias-merge-to-][Alias ~#merge~ to ~<<~]]
|
|
59
|
+
- [[#numeric][Numeric]]
|
|
60
|
+
- [[#method-signum][Method ~#signum~]]
|
|
61
|
+
- [[#method-commasplaces--nil][Method ~#commas(places = nil)~]]
|
|
62
|
+
- [[#methods-whole-and-int_if_whole][Methods ~#whole?~ and ~#int_if_whole~]]
|
|
63
|
+
- [[#method-secs_to_hms][Method ~#secs_to_hms~]]
|
|
64
|
+
- [[#range][Range]]
|
|
65
|
+
- [[#methods-contiguous-left_contiguous-right_contiguous][Methods ~#contiguous~, ~#left_contiguous~, ~#right_contiguous~]]
|
|
66
|
+
- [[#method-joinother][Method ~#join(other)~]]
|
|
67
|
+
- [[#method-spanned_byothers][Method ~#spanned_by?(others)~]]
|
|
68
|
+
- [[#methods-gapsothers-overlapsothers][Methods ~#gaps(others)~, ~#overlaps(others)~]]
|
|
69
|
+
- [[#string][String]]
|
|
70
|
+
- [[#method-fuzzy_match][Method ~#fuzzy_match~]]
|
|
71
|
+
- [[#method-matches_with][Method ~#matches_with~]]
|
|
72
|
+
- [[#method-entitle][Method ~#entitle~]]
|
|
73
|
+
- [[#method-distance][Method ~#distance~]]
|
|
74
|
+
- [[#method-commasplaces][Method ~#commas(places)~]]
|
|
75
|
+
- [[#method-wrapwidth-hang][Method =#wrap(width, hang)=]]
|
|
76
|
+
- [[#method-as_sym][Method =#as_sym=]]
|
|
77
|
+
- [[#symbol][Symbol]]
|
|
78
|
+
- [[#method-as_str][Method =#as_str=]]
|
|
79
|
+
- [[#tex-quoting][TeX Quoting]]
|
|
80
|
+
- [[#contributing][Contributing]]
|
|
81
|
+
|
|
82
|
+
* Version
|
|
83
|
+
#+begin_src ruby
|
|
84
|
+
require_relative './lib/fat_core/version'
|
|
85
|
+
"Current version is: #{FatCore::VERSION}"
|
|
86
|
+
#+end_src
|
|
87
|
+
|
|
88
|
+
#+begin_example
|
|
89
|
+
Current version is: 7.0.0
|
|
90
|
+
#+end_example
|
|
91
|
+
|
|
39
92
|
* FatCore
|
|
40
93
|
|
|
41
|
-
~fat-core~ is a
|
|
42
|
-
|
|
94
|
+
~fat-core~ is somewhat of a grab bag of core class extensions that I have
|
|
95
|
+
found useful across several projects. It's higgeldy-piggeldy nature reflects
|
|
96
|
+
the fact that none of them are important enough to deserve a gem of their own,
|
|
97
|
+
but nonetheless need to be collected in one place to reduce redundancy across
|
|
98
|
+
projects and provide a focused place to develop and test them.
|
|
43
99
|
|
|
44
|
-
|
|
100
|
+
* Installation
|
|
45
101
|
|
|
46
102
|
Add this line to your application's Gemfile:
|
|
47
103
|
|
|
48
|
-
#+
|
|
49
|
-
gem 'fat_core'
|
|
104
|
+
#+begin_src ruby
|
|
105
|
+
gem 'fat_core'
|
|
50
106
|
#+end_SRC
|
|
51
107
|
|
|
52
108
|
And then execute:
|
|
@@ -61,7 +117,7 @@ Or install it yourself as:
|
|
|
61
117
|
$ gem install fat_core
|
|
62
118
|
#+end_src
|
|
63
119
|
|
|
64
|
-
|
|
120
|
+
* Usage
|
|
65
121
|
|
|
66
122
|
You can extend classes individually by requiring the corresponding file:
|
|
67
123
|
|
|
@@ -86,50 +142,642 @@ Or, you can require them all:
|
|
|
86
142
|
Many of these have little that is of general interest, but there are a few
|
|
87
143
|
goodies.
|
|
88
144
|
|
|
89
|
-
***
|
|
145
|
+
*** Array
|
|
146
|
+
**** Method ~#comma_join(sep: nil, last_sep: nil, two_sep: nil)~
|
|
147
|
+
Convert this array into a single string by (1) applying ~#to_s~ to each
|
|
148
|
+
element and (2) joining the elements with the string given by the ~sep:~
|
|
149
|
+
parameter. By default the sep parameter is ', '.
|
|
90
150
|
|
|
91
|
-
You
|
|
92
|
-
|
|
93
|
-
contiguity of Ranges to one another (~#contiguous?~, ~#left_contiguous?~, and
|
|
94
|
-
~#right_contiguous?~, ~#join~), and the testing of overlaps between ranges
|
|
95
|
-
(~#overlaps?~, ~#overlaps_among?~). These are put to good use in the
|
|
96
|
-
'fat_period' ([[https://github.com/ddoherty03/fat_period]]) gem, which combines
|
|
97
|
-
fat_core's extended Range class with its extended Date class to make a useful
|
|
98
|
-
Period class for date ranges, and you may find fat_core's extended Range class
|
|
99
|
-
likewise useful.
|
|
151
|
+
You may use a different separation string in the case when there are only two
|
|
152
|
+
items in the list by supplying a ~two_sep~ parameter.
|
|
100
153
|
|
|
101
|
-
|
|
102
|
-
|
|
154
|
+
You may also supply a difference separation string to separate the second-last
|
|
155
|
+
and last items in the array by supplying a ~last_sep:~ parameter.
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
157
|
+
By default, the sep parameter is the string ', ', the ~two_sep~ is ' and ',
|
|
158
|
+
and the ~last_sep~ is ', and ', all of which makes for a well-punctuated
|
|
159
|
+
English clause.
|
|
160
|
+
|
|
161
|
+
If ~sep~ is given, the other two parameters are set to its
|
|
162
|
+
value by default. If ~last_sep~ is given, ~two_sep~ takes its value by
|
|
163
|
+
default.
|
|
164
|
+
|
|
165
|
+
If the input array is empty, ~#comma_join~ returns an empty string.
|
|
166
|
+
|
|
167
|
+
#+begin_src ruby
|
|
168
|
+
require_relative 'lib/fat_core/array'
|
|
169
|
+
|
|
170
|
+
%w{hammers nails glue bolts}.comma_join
|
|
171
|
+
#+end_src
|
|
172
|
+
|
|
173
|
+
#+begin_example
|
|
174
|
+
hammers, nails, glue, and bolts
|
|
175
|
+
#+end_example
|
|
176
|
+
|
|
177
|
+
#+begin_src ruby
|
|
178
|
+
require_relative 'lib/fat_core/array'
|
|
179
|
+
|
|
180
|
+
%w{hammers nails}.comma_join
|
|
181
|
+
#+end_src
|
|
182
|
+
|
|
183
|
+
#+begin_example
|
|
184
|
+
hammers and nails
|
|
185
|
+
#+end_example
|
|
186
|
+
|
|
187
|
+
And, if you are ideologically opposed to the Oxford comma:
|
|
188
|
+
|
|
189
|
+
#+begin_src ruby
|
|
190
|
+
require_relative 'lib/fat_core/array'
|
|
191
|
+
|
|
192
|
+
%w{hammers nails glue bolts}.comma_join(last_sep: ' and ')
|
|
193
|
+
#+end_src
|
|
194
|
+
|
|
195
|
+
#+begin_example
|
|
196
|
+
hammers, nails, glue and bolts
|
|
197
|
+
#+end_example
|
|
198
|
+
|
|
199
|
+
**** Method ~#last_i~
|
|
200
|
+
Return the index of the last element of the Array.
|
|
201
|
+
|
|
202
|
+
#+begin_src ruby
|
|
203
|
+
require_relative 'lib/fat_core/array'
|
|
204
|
+
|
|
205
|
+
%w{hammers nails glue bolts}.last_i
|
|
206
|
+
#+end_src
|
|
207
|
+
|
|
208
|
+
#+begin_example
|
|
209
|
+
3
|
|
210
|
+
#+end_example
|
|
211
|
+
|
|
212
|
+
**** Method ~#intersect_with_dups~
|
|
213
|
+
Return a new Array that is the intersection of this Array with all ~others~,
|
|
214
|
+
but without removing duplicates as the ~Array#&~ method does. All items of
|
|
215
|
+
this Array are included in the result but only if they also appear in all of
|
|
216
|
+
the other Arrays.
|
|
217
|
+
|
|
218
|
+
#+begin_src ruby
|
|
219
|
+
require_relative 'lib/fat_core/array'
|
|
220
|
+
|
|
221
|
+
a = %w{hammers nails glue bolts nails}
|
|
222
|
+
b = %w{nails fingers knuckles nails}
|
|
223
|
+
a.intersect_with_dups(b)
|
|
224
|
+
#+end_src
|
|
225
|
+
|
|
226
|
+
#+begin_example
|
|
227
|
+
| nails | nails |
|
|
228
|
+
#+end_example
|
|
229
|
+
|
|
230
|
+
**** Method ~diff_with_dups~
|
|
231
|
+
Return an Array that is the difference between this Array and =other=, but
|
|
232
|
+
without removing duplicates as the Array#- method does. All items of this
|
|
233
|
+
Array are included in the result /unless/ they also appear in any of the
|
|
234
|
+
=other= Arrays.
|
|
235
|
+
|
|
236
|
+
#+begin_src ruby
|
|
237
|
+
require_relative 'lib/fat_core/array'
|
|
238
|
+
|
|
239
|
+
a = %w{hammers nails glue bolts hammers nails}
|
|
240
|
+
b = %w{nails fingers knuckles nails}
|
|
241
|
+
a.diff_with_dups(b)
|
|
242
|
+
#+end_src
|
|
243
|
+
|
|
244
|
+
#+begin_example
|
|
245
|
+
| hammers | glue | bolts | hammers |
|
|
246
|
+
#+end_example
|
|
247
|
+
|
|
248
|
+
*** BigDecimal ~#inspect~
|
|
249
|
+
~FatCore~ provides nothing but a better ~#inspect~ method for the ~BigDecimal~
|
|
250
|
+
class since the default inspect method is not very readable.
|
|
251
|
+
|
|
252
|
+
#+begin_src ruby
|
|
253
|
+
require_relative 'lib/fat_core/bigdecimal'
|
|
254
|
+
|
|
255
|
+
BigDecimal('2.1718281828').inspect
|
|
256
|
+
#+end_src
|
|
257
|
+
|
|
258
|
+
#+begin_example
|
|
259
|
+
2.1718281828
|
|
260
|
+
#+end_example
|
|
261
|
+
|
|
262
|
+
Without ~FatCore~, the result is "0.2718281828e1", forcing you to interpret
|
|
263
|
+
the exponent to understand where the decimal place is.
|
|
108
264
|
|
|
109
265
|
*** Enumerable
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
266
|
+
**** Method ~#each_with_flags~
|
|
267
|
+
~FatCore::Enumerable~ extends ~Enumerable~ with the ~#each_with_flags~ method
|
|
268
|
+
that yields the elements of the ~Enumerable~ but also yields two booleans,
|
|
269
|
+
~first~ and ~last~ that are set to ~true~ on respectively, the first and last
|
|
270
|
+
element of the Enumerable and ~false~ otherwise. This makes it easy to treat
|
|
271
|
+
these two cases specially without testing the index as in ~#each_with_index~.
|
|
272
|
+
|
|
273
|
+
#+begin_src ruby
|
|
274
|
+
require_relative 'lib/fat_core/enumerable'
|
|
275
|
+
|
|
276
|
+
result = []
|
|
277
|
+
fibs = %w{1, 1, 2, 3, 5, 8, 13, 21}
|
|
278
|
+
fibs.each_with_flags do |f, first, last|
|
|
279
|
+
result <<
|
|
280
|
+
if first
|
|
281
|
+
["Start", f]
|
|
282
|
+
elsif last
|
|
283
|
+
["Last", f]
|
|
284
|
+
else
|
|
285
|
+
["Continue", f]
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
result
|
|
289
|
+
#+end_src
|
|
290
|
+
|
|
291
|
+
#+begin_example
|
|
292
|
+
| Start | 1, |
|
|
293
|
+
| Continue | 1, |
|
|
294
|
+
| Continue | 2, |
|
|
295
|
+
| Continue | 3, |
|
|
296
|
+
| Continue | 5, |
|
|
297
|
+
| Continue | 8, |
|
|
298
|
+
| Continue | 13, |
|
|
299
|
+
| Last | 21 |
|
|
300
|
+
#+end_example
|
|
115
301
|
|
|
116
302
|
*** Hash
|
|
303
|
+
FatCore::Hash extends the Hash class with some useful methods.
|
|
117
304
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
~#each_with_flags~.
|
|
305
|
+
**** Method ~#each_pair_with_flags~
|
|
306
|
+
As with the extension for ~Enumerables~, ~FatCore~ provides a method for
|
|
307
|
+
enumerating the key-value pair of the ~Hash~ with flags that are set ~true~
|
|
308
|
+
for the first and last elements but ~false~ otherwise:
|
|
123
309
|
|
|
124
|
-
|
|
125
|
-
|
|
310
|
+
#+begin_src ruby
|
|
311
|
+
require_relative './lib/fat_core/hash'
|
|
126
312
|
|
|
127
|
-
|
|
128
|
-
|
|
313
|
+
h = {'Chaucer' => 'Cantebury Tales', 'Shakespeare' => 'The Merchant of Venice',
|
|
314
|
+
'Austen' => 'Pride and Prejudice', 'C. Brontë' => 'Jane Eyre',
|
|
315
|
+
'E. Brontë' => 'Wuthering Heights' }
|
|
316
|
+
result = []
|
|
317
|
+
result << ['Position', 'Author', 'Novel']
|
|
318
|
+
result << nil
|
|
319
|
+
h.each_pair_with_flags do |k, v, first, last|
|
|
320
|
+
pos=
|
|
321
|
+
if first
|
|
322
|
+
'Begin'
|
|
323
|
+
elsif last
|
|
324
|
+
'End'
|
|
325
|
+
else
|
|
326
|
+
'Middle'
|
|
327
|
+
end
|
|
328
|
+
result << [pos, k, v]
|
|
329
|
+
end
|
|
330
|
+
result
|
|
129
331
|
#+end_src
|
|
130
332
|
|
|
131
|
-
|
|
333
|
+
#+begin_example
|
|
334
|
+
| Position | Author | Novel |
|
|
335
|
+
|----------+-------------+------------------------|
|
|
336
|
+
| Begin | Chaucer | Cantebury Tales |
|
|
337
|
+
| Middle | Shakespeare | The Merchant of Venice |
|
|
338
|
+
| Middle | Austen | Pride and Prejudice |
|
|
339
|
+
| Middle | C. Brontë | Jane Eyre |
|
|
340
|
+
| End | E. Brontë | Wuthering Heights |
|
|
341
|
+
#+end_example
|
|
342
|
+
|
|
343
|
+
**** Method ~#delete_with_value~ and ~#delete_with_value!~
|
|
344
|
+
This method modifies a ~Hash~ by deleting the key-value pairs when the value
|
|
345
|
+
equals the given value or values:
|
|
346
|
+
|
|
347
|
+
#+begin_src ruby :results output
|
|
348
|
+
require_relative './lib/fat_core/hash'
|
|
349
|
+
|
|
350
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
351
|
+
h.delete_with_value!(2)
|
|
352
|
+
puts h
|
|
353
|
+
#+end_src
|
|
354
|
+
|
|
355
|
+
#+begin_example
|
|
356
|
+
{:a=>1, :c=>3, :e=>1}
|
|
357
|
+
#+end_example
|
|
358
|
+
|
|
359
|
+
You can supply multiple values for deletion:
|
|
360
|
+
|
|
361
|
+
#+begin_src ruby :results output
|
|
362
|
+
require_relative './lib/fat_core/hash'
|
|
363
|
+
|
|
364
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
365
|
+
h.delete_with_value!(1, 3)
|
|
366
|
+
puts h
|
|
367
|
+
#+end_src
|
|
368
|
+
|
|
369
|
+
#+begin_example
|
|
370
|
+
{:b=>2, :d=>2}
|
|
371
|
+
#+end_example
|
|
372
|
+
|
|
373
|
+
The non-bang method returns a clone of the Hash with the given deletions made:
|
|
374
|
+
|
|
375
|
+
#+begin_src ruby :results output
|
|
376
|
+
require_relative './lib/fat_core/hash'
|
|
132
377
|
|
|
378
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
379
|
+
h2 = h.delete_with_value(1, 3)
|
|
380
|
+
puts h
|
|
381
|
+
puts h2
|
|
382
|
+
#+end_src
|
|
383
|
+
|
|
384
|
+
#+begin_example
|
|
385
|
+
{:a=>1, :b=>2, :c=>3, :d=>2, :e=>1}
|
|
386
|
+
{:b=>2, :d=>2}
|
|
387
|
+
#+end_example
|
|
388
|
+
|
|
389
|
+
**** Method ~#keys_with_value~
|
|
390
|
+
Return an ~Array~ of keys of the ~Hash~ with a value ~==~ to the given value
|
|
391
|
+
or values.
|
|
392
|
+
|
|
393
|
+
#+begin_src ruby :results output
|
|
394
|
+
require_relative './lib/fat_core/hash'
|
|
395
|
+
|
|
396
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
397
|
+
puts h.keys_with_value(1).inspect
|
|
398
|
+
puts h.keys_with_value(2, 3).inspect
|
|
399
|
+
#+end_src
|
|
400
|
+
|
|
401
|
+
#+begin_example
|
|
402
|
+
[:a, :e]
|
|
403
|
+
[:b, :d, :c]
|
|
404
|
+
#+end_example
|
|
405
|
+
|
|
406
|
+
**** Method ~#remap_keys~
|
|
407
|
+
This method pre-dates the new ~#transform_keys~ method now available for
|
|
408
|
+
~Hash~, but it is kept as an alternative. It takes a ~Hash~ as an argument
|
|
409
|
+
that maps existing keys to their replacement in the resulting ~Hash~. The
|
|
410
|
+
original ~Hash~ is not effected.
|
|
411
|
+
|
|
412
|
+
#+begin_src ruby :results output
|
|
413
|
+
require_relative './lib/fat_core/hash'
|
|
414
|
+
|
|
415
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
416
|
+
puts h.remap_keys({:a => :A, :b => :B}).inspect
|
|
417
|
+
#+end_src
|
|
418
|
+
|
|
419
|
+
#+begin_example
|
|
420
|
+
{:A=>1, :B=>2, :c=>3, :d=>2, :e=>1}
|
|
421
|
+
#+end_example
|
|
422
|
+
|
|
423
|
+
These days, a more systematic job could be done with ~#transform_keys~:
|
|
424
|
+
#+begin_src ruby :results output
|
|
425
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
426
|
+
puts h.transform_keys { |k| k.to_s.upcase.to_sym }.inspect
|
|
427
|
+
#+end_src
|
|
428
|
+
|
|
429
|
+
#+begin_example
|
|
430
|
+
{:A=>1, :B=>2, :C=>3, :D=>2, :E=>1}
|
|
431
|
+
#+end_example
|
|
432
|
+
|
|
433
|
+
**** Method ~#replace_keys~
|
|
434
|
+
A wholesale replacement of the existing keys can be done with this method:
|
|
435
|
+
|
|
436
|
+
#+begin_src ruby :results output
|
|
437
|
+
require_relative './lib/fat_core/hash'
|
|
438
|
+
|
|
439
|
+
h = { a: 1, b: 2, c: 3, d: 2, e: 1 }
|
|
440
|
+
puts h
|
|
441
|
+
puts h.replace_keys([:z, :y, :x, :w, :v]).inspect
|
|
442
|
+
#+end_src
|
|
443
|
+
|
|
444
|
+
#+begin_example
|
|
445
|
+
{:a=>1, :b=>2, :c=>3, :d=>2, :e=>1}
|
|
446
|
+
{:z=>1, :y=>2, :x=>3, :w=>2, :v=>1}
|
|
447
|
+
#+end_example
|
|
448
|
+
|
|
449
|
+
**** Alias ~#merge~ to ~<<~
|
|
450
|
+
Finally, ~FatCore~ adds the "shovel" operator as an alias for ~#merge~ to
|
|
451
|
+
provide a pretty way to represent the merger of the right ~Hash~ into the left
|
|
452
|
+
~Hash~:
|
|
453
|
+
|
|
454
|
+
#+begin_src ruby :results output
|
|
455
|
+
require_relative './lib/fat_core/hash'
|
|
456
|
+
|
|
457
|
+
h = {a: 'A', b: 'B', c: 'C'} << {c: 'CC', d: 'DD'} << {d: 'DDD', e: 'EEE'}
|
|
458
|
+
puts h
|
|
459
|
+
#+end_src
|
|
460
|
+
|
|
461
|
+
#+begin_example
|
|
462
|
+
{:a=>"A", :b=>"B", :c=>"CC", :d=>"DDD", :e=>"EEE"}
|
|
463
|
+
#+end_example
|
|
464
|
+
|
|
465
|
+
It groups values into pairs and applies the ~#to_h~ method to the right-hand
|
|
466
|
+
argument if it is an ~Enumerable~, so it also works if the right-hand argument
|
|
467
|
+
is an ~Array~ or ~Enumerable~:
|
|
468
|
+
|
|
469
|
+
#+begin_src ruby :results output
|
|
470
|
+
require 'fileutils'
|
|
471
|
+
require_relative './lib/fat_core/hash'
|
|
472
|
+
|
|
473
|
+
FileUtils.mkdir_p('./tmp')
|
|
474
|
+
ff = File.open('./tmp/junk', 'w')
|
|
475
|
+
ff.write("f\n", "FFFF\n", "g\n", "GGGG\n")
|
|
476
|
+
ff.close
|
|
477
|
+
ff = File.open('./tmp/junk', 'r')
|
|
478
|
+
h = {a: 'A', b: 'B', c: 'C'} <<
|
|
479
|
+
[:c, 'CC', :d, 'DD'] <<
|
|
480
|
+
{d: 'DDD', e: 'EEE'} <<
|
|
481
|
+
ff.readlines.map(&:chomp) <<
|
|
482
|
+
[[:h, 'HHHHH'], [:j, 'JJJJJ']]
|
|
483
|
+
# h.transform_keys!(&:to_sym)
|
|
484
|
+
ff.close
|
|
485
|
+
FileUtils.rm_rf('./tmp/junk')
|
|
486
|
+
puts h
|
|
487
|
+
#+end_src
|
|
488
|
+
|
|
489
|
+
#+begin_example
|
|
490
|
+
{:a=>"A", :b=>"B", :c=>"CC", :d=>"DDD", :e=>"EEE", "f"=>"FFFF", "g"=>"GGGG", :h=>"HHHHH", :j=>"JJJJJ"}
|
|
491
|
+
#+end_example
|
|
492
|
+
|
|
493
|
+
*** Numeric
|
|
494
|
+
**** Method ~#signum~
|
|
495
|
+
Return ~-1~ for negative numbers, ~0~ for zero, and ~+1~ for positive numbers.
|
|
496
|
+
This is sometimes handy.
|
|
497
|
+
|
|
498
|
+
**** Method ~#commas(places = nil)~
|
|
499
|
+
To get s ~String~ representation of a ~Numeric~ with grouping commas inserted,
|
|
500
|
+
~FatCore~ provides the ~#commas~ method:
|
|
501
|
+
|
|
502
|
+
#+begin_src ruby
|
|
503
|
+
require_relative 'lib/fat_core/numeric'
|
|
504
|
+
result = []
|
|
505
|
+
result << ['N', 'Places', 'N.commas(places)']
|
|
506
|
+
result << nil
|
|
507
|
+
nums = [3.14159, 2.718281828, 100000, 0.0059, 16236565468798.66877]
|
|
508
|
+
places = [0, 3, 5]
|
|
509
|
+
nums.each do |n|
|
|
510
|
+
places.each do |pl|
|
|
511
|
+
result << [n, pl, n.commas(pl)]
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
result
|
|
515
|
+
#+end_src
|
|
516
|
+
|
|
517
|
+
#+begin_example
|
|
518
|
+
| N | Places | N.commas(places) |
|
|
519
|
+
|--------------------+--------+--------------------------|
|
|
520
|
+
| 3.14159 | 0 | 3 |
|
|
521
|
+
| 3.14159 | 3 | 3.142 |
|
|
522
|
+
| 3.14159 | 5 | 3.14159 |
|
|
523
|
+
| 2.718281828 | 0 | 3 |
|
|
524
|
+
| 2.718281828 | 3 | 2.718 |
|
|
525
|
+
| 2.718281828 | 5 | 2.71828 |
|
|
526
|
+
| 100000 | 0 | 100,000 |
|
|
527
|
+
| 100000 | 3 | 100,000.000 |
|
|
528
|
+
| 100000 | 5 | 100,000.00000 |
|
|
529
|
+
| 0.0059 | 0 | 0 |
|
|
530
|
+
| 0.0059 | 3 | 0.006 |
|
|
531
|
+
| 0.0059 | 5 | 0.00590 |
|
|
532
|
+
| 16236565468798.668 | 0 | 16,236,565,468,799 |
|
|
533
|
+
| 16236565468798.668 | 3 | 16,236,565,468,798.668 |
|
|
534
|
+
| 16236565468798.668 | 5 | 16,236,565,468,798.66800 |
|
|
535
|
+
#+end_example
|
|
536
|
+
|
|
537
|
+
FatCore::Numeric has methods for inserting grouping commas into a number
|
|
538
|
+
(~#commas~ and ~#group~), for converting seconds to HH:MM:SS.dd format
|
|
539
|
+
(~#secs_to_hms~), for testing for integrality (~#whole?~ and ~#int_if_whole~), and
|
|
540
|
+
testing for sign (~#signum~).
|
|
541
|
+
|
|
542
|
+
**** Methods ~#whole?~ and ~#int_if_whole~
|
|
543
|
+
At times it is useful to know if a Float or BigDecimal can be converted to an
|
|
544
|
+
~Integer~ without losing precision.
|
|
545
|
+
|
|
546
|
+
#+begin_src ruby
|
|
547
|
+
require_relative 'lib/fat_core/numeric'
|
|
548
|
+
result = []
|
|
549
|
+
result << ['N', '#whole?', '#int_if_whole', 'Classes']
|
|
550
|
+
result << nil
|
|
551
|
+
nums = [3.14159, 3.000000, 100000, 0.0059, 16236565468798.66877]
|
|
552
|
+
nums.each do |n|
|
|
553
|
+
result << [n, n.whole?, n.int_if_whole, "#{n.class} -> #{n.int_if_whole.class}"]
|
|
554
|
+
end
|
|
555
|
+
result
|
|
556
|
+
#+end_src
|
|
557
|
+
|
|
558
|
+
#+begin_example
|
|
559
|
+
| N | #whole? | #int_if_whole | Classes |
|
|
560
|
+
|--------------------+---------+--------------------+--------------------|
|
|
561
|
+
| 3.14159 | false | 3.14159 | Float -> Float |
|
|
562
|
+
| 3.0 | true | 3 | Float -> Integer |
|
|
563
|
+
| 100000 | true | 100000 | Integer -> Integer |
|
|
564
|
+
| 0.0059 | false | 0.0059 | Float -> Float |
|
|
565
|
+
| 16236565468798.668 | false | 16236565468798.668 | Float -> Float |
|
|
566
|
+
#+end_example
|
|
567
|
+
|
|
568
|
+
**** Method ~#secs_to_hms~
|
|
569
|
+
This method converts a numeric representing a number of seconds or an angle in
|
|
570
|
+
degrees to a ~String~ of the form "HH:MM:SS" representing the same quantity in
|
|
571
|
+
hours, minutes, and seconds.
|
|
572
|
+
|
|
573
|
+
#+begin_src ruby
|
|
574
|
+
require_relative 'lib/fat_core/numeric'
|
|
575
|
+
result = []
|
|
576
|
+
result << ['N', 'HH:MM:SS']
|
|
577
|
+
result << nil
|
|
578
|
+
nums = [85777.66, 959.66, -1198.33, 0, 3.14159 * 180]
|
|
579
|
+
nums.each do |n|
|
|
580
|
+
result << [n, n.secs_to_hms]
|
|
581
|
+
end
|
|
582
|
+
result
|
|
583
|
+
#+end_src
|
|
584
|
+
|
|
585
|
+
#+begin_example
|
|
586
|
+
| N | HH:MM:SS |
|
|
587
|
+
|-------------------+-------------|
|
|
588
|
+
| 85777.66 | 23:49:37.66 |
|
|
589
|
+
| 959.66 | 00:15:59.66 |
|
|
590
|
+
| -1198.33 | -1:40:01.67 |
|
|
591
|
+
| 0 | 00:00:00 |
|
|
592
|
+
| 565.4861999999999 | 00:09:25.48 |
|
|
593
|
+
#+end_example
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
*** Range
|
|
597
|
+
~FatCore~ can also extend the Range class with several useful methods that
|
|
598
|
+
emphasize coverage of one range by one or more others (~#spanned_by?~ and
|
|
599
|
+
~#gaps~), contiguity of Ranges to one another (~#contiguous?~,
|
|
600
|
+
~#left_contiguous?~, and ~#right_contiguous?~, ~#join~), and the testing of
|
|
601
|
+
overlaps between ranges (~#overlaps?~, ~#overlaps_among?~). These are put to
|
|
602
|
+
good use in the 'fat_period' ([[https://github.com/ddoherty03/fat_period]]) gem,
|
|
603
|
+
which combines fat_core's extended Range class with its extended Date class to
|
|
604
|
+
make a useful Period class for date ranges, and you may find fat_core's
|
|
605
|
+
extended Range class likewise useful.
|
|
606
|
+
|
|
607
|
+
**** Methods ~#contiguous~, ~#left_contiguous~, ~#right_contiguous~
|
|
608
|
+
These methods determine whether the subject ~Range~ are "contiguous" with
|
|
609
|
+
another ~Range~ on the left, right, or either side. The notion of contiguity
|
|
610
|
+
is different for ~Ranges~ whose min and max values respond to the ~#succ~
|
|
611
|
+
method: if they do, "contiguity" only requires that the ~#succ~ of the max
|
|
612
|
+
value of the left range equal the min value of the right ~Range~; otherwise
|
|
613
|
+
the max value of the left ~Range~ must equal the min value of the right
|
|
614
|
+
~Range~.
|
|
615
|
+
|
|
616
|
+
#+begin_src ruby
|
|
617
|
+
require_relative 'lib/fat_core/range'
|
|
618
|
+
require 'date'
|
|
619
|
+
|
|
620
|
+
result = []
|
|
621
|
+
result << ["Self", "Other", "Contiguous?", "Right?", "Left?"]
|
|
622
|
+
result << nil
|
|
623
|
+
pairs = [
|
|
624
|
+
[(0..10), (11..12)],
|
|
625
|
+
[(11..20), (0..10)],
|
|
626
|
+
[(0..10), (15..20)],
|
|
627
|
+
[(3.145..12.3), (0.5..3.145)],
|
|
628
|
+
[(3.146..12.3), (0.5..3.145)],
|
|
629
|
+
[('a'..'q'), ('r'..'z')],
|
|
630
|
+
[('a'..'q'), ('s'..'z')],
|
|
631
|
+
[(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-04')..Date.today)],
|
|
632
|
+
[(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-28')..Date.today)]
|
|
633
|
+
]
|
|
634
|
+
pairs.each do |r1, r2|
|
|
635
|
+
result << [r1.to_s, r2.to_s, r1.contiguous?(r2), r1.right_contiguous?(r2), r1.left_contiguous?(r2)]
|
|
636
|
+
end
|
|
637
|
+
result
|
|
638
|
+
#+end_src
|
|
639
|
+
|
|
640
|
+
#+begin_example
|
|
641
|
+
| Self | Other | Contiguous? | Right? | Left? |
|
|
642
|
+
|------------------------+------------------------+-------------+--------+-------|
|
|
643
|
+
| 0..10 | 11..12 | true | true | false |
|
|
644
|
+
| 11..20 | 0..10 | true | false | true |
|
|
645
|
+
| 0..10 | 15..20 | false | false | false |
|
|
646
|
+
| 3.145..12.3 | 0.5..3.145 | true | false | true |
|
|
647
|
+
| 3.146..12.3 | 0.5..3.145 | false | false | false |
|
|
648
|
+
| a..q | r..z | true | true | false |
|
|
649
|
+
| a..q | s..z | false | false | false |
|
|
650
|
+
| 1963-11-22..1964-11-03 | 1964-11-04..2025-11-22 | true | true | false |
|
|
651
|
+
| 1963-11-22..1964-11-03 | 1964-11-28..2025-11-22 | false | false | false |
|
|
652
|
+
#+end_example
|
|
653
|
+
|
|
654
|
+
**** Method ~#join(other)~
|
|
655
|
+
If ~self~ is contiguous with ~other~, return a new ~Range~ that splices the
|
|
656
|
+
two ~Range~s into one ~Range~.
|
|
657
|
+
|
|
658
|
+
#+begin_src ruby
|
|
659
|
+
require_relative 'lib/fat_core/range'
|
|
660
|
+
require 'date'
|
|
661
|
+
|
|
662
|
+
result = []
|
|
663
|
+
result << ["Self", "Other", "Contiguous?", "Joined"]
|
|
664
|
+
result << nil
|
|
665
|
+
pairs = [
|
|
666
|
+
[(0..10), (11..12)],
|
|
667
|
+
[(11..20), (0..10)],
|
|
668
|
+
[(0..10), (15..20)],
|
|
669
|
+
[(3.145..12.3), (0.5..3.145)],
|
|
670
|
+
[(3.146..12.3), (0.5..3.145)],
|
|
671
|
+
[('a'..'q'), ('r'..'z')],
|
|
672
|
+
[('a'..'q'), ('s'..'z')],
|
|
673
|
+
[(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-04')..Date.today)],
|
|
674
|
+
[(Date.parse('1963-11-22')..Date.parse('1964-11-03')), (Date.parse('1964-11-28')..Date.today)]
|
|
675
|
+
]
|
|
676
|
+
pairs.each do |r1, r2|
|
|
677
|
+
result << [r1.to_s, r2.to_s, r1.contiguous?(r2), "#{r1.join(r2)}"]
|
|
678
|
+
end
|
|
679
|
+
result
|
|
680
|
+
#+end_src
|
|
681
|
+
|
|
682
|
+
#+begin_example
|
|
683
|
+
| Self | Other | Contiguous? | Joined |
|
|
684
|
+
|------------------------+------------------------+-------------+------------------------|
|
|
685
|
+
| 0..10 | 11..12 | true | 0..12 |
|
|
686
|
+
| 11..20 | 0..10 | true | 0..20 |
|
|
687
|
+
| 0..10 | 15..20 | false | |
|
|
688
|
+
| 3.145..12.3 | 0.5..3.145 | true | 0.5..12.3 |
|
|
689
|
+
| 3.146..12.3 | 0.5..3.145 | false | |
|
|
690
|
+
| a..q | r..z | true | a..z |
|
|
691
|
+
| a..q | s..z | false | |
|
|
692
|
+
| 1963-11-22..1964-11-03 | 1964-11-04..2025-11-22 | true | 1963-11-22..2025-11-22 |
|
|
693
|
+
| 1963-11-22..1964-11-03 | 1964-11-28..2025-11-22 | false | |
|
|
694
|
+
#+end_example
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
**** Method ~#spanned_by?(others)~
|
|
698
|
+
A set of ~Ranges~ "spans" a given ~Range~ if the set is contiguous and fully
|
|
699
|
+
covers the given ~Range~ with no overlaps and no gaps. A set that over-covers
|
|
700
|
+
the given ~Range~ is still considered to span it, even though it is wider than
|
|
701
|
+
the given ~Range~. In other words, a set spans the given ~Range~ if the set
|
|
702
|
+
can be joined and the given ~Range~ is within the joined ~Range~.
|
|
703
|
+
|
|
704
|
+
#+begin_src ruby
|
|
705
|
+
require_relative 'lib/fat_core/range'
|
|
706
|
+
require 'date'
|
|
707
|
+
|
|
708
|
+
result = []
|
|
709
|
+
result << ["Self", "Others", "Spanned By?"]
|
|
710
|
+
result << nil
|
|
711
|
+
pairs = [
|
|
712
|
+
[(0..10), [(-1..5), (6..10)]],
|
|
713
|
+
[(1..20), [(0..10), (11..20)]],
|
|
714
|
+
[(1..20), [(0..10), (10..20)]],
|
|
715
|
+
[(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.5..13.5)]],
|
|
716
|
+
[(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.6..13.5)]],
|
|
717
|
+
[('a'..'z'), [('a'..'g'), ('h'..'s'), ('t'..'z')]],
|
|
718
|
+
[('a'..'z'), [('a'..'g'), ('j'..'s'), ('t'..'z')]],
|
|
719
|
+
]
|
|
720
|
+
pairs.each do |r, others|
|
|
721
|
+
result << ["#{r}", "#{others}", r.spanned_by?(others)]
|
|
722
|
+
end
|
|
723
|
+
result
|
|
724
|
+
#+end_src
|
|
725
|
+
|
|
726
|
+
#+begin_example
|
|
727
|
+
| Self | Others | Spanned By? |
|
|
728
|
+
|-------------+-------------------------------------+-------------|
|
|
729
|
+
| 0..10 | [-1..5, 6..10] | true |
|
|
730
|
+
| 1..20 | [0..10, 11..20] | true |
|
|
731
|
+
| 1..20 | [0..10, 10..20] | false |
|
|
732
|
+
| 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.5..13.5] | true |
|
|
733
|
+
| 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.6..13.5] | false |
|
|
734
|
+
| a..z | ["a".."g", "h".."s", "t".."z"] | true |
|
|
735
|
+
| a..z | ["a".."g", "j".."s", "t".."z"] | false |
|
|
736
|
+
#+end_example
|
|
737
|
+
|
|
738
|
+
**** Methods ~#gaps(others)~, ~#overlaps(others)~
|
|
739
|
+
When the set of other ~Ranges~ does not span the given ~Range~, these methods
|
|
740
|
+
return an set of ~Ranges~ that represent the portions of the given ~Range~ no
|
|
741
|
+
covered by the ~others~, the "gaps", or the points within the given ~Range~
|
|
742
|
+
where the ~others~ overlap one another and thus are not contiguous.
|
|
743
|
+
|
|
744
|
+
#+begin_src ruby
|
|
745
|
+
require_relative 'lib/fat_core/range'
|
|
746
|
+
require 'date'
|
|
747
|
+
|
|
748
|
+
result = []
|
|
749
|
+
result << ["Self", "Others", "Spanned By?", "Gaps", "Overlaps"]
|
|
750
|
+
result << nil
|
|
751
|
+
pairs = [
|
|
752
|
+
[(0..10), [(-1..5), (6..10)]],
|
|
753
|
+
[(1..20), [(0..10), (11..20)]],
|
|
754
|
+
[(1..20), [(0..15), (11..20)]],
|
|
755
|
+
[(1..20), [(0..10), (10..20)]],
|
|
756
|
+
[(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.5..13.5)]],
|
|
757
|
+
[(3.145..12.3), [(0.5..3.45), (3.45..10.5), (10.6..13.5)]],
|
|
758
|
+
[('a'..'z'), [('a'..'g'), ('h'..'s'), ('t'..'z')]],
|
|
759
|
+
[('a'..'z'), [('a'..'g'), ('j'..'s'), ('t'..'z')]],
|
|
760
|
+
]
|
|
761
|
+
pairs.each do |r, others|
|
|
762
|
+
result << ["#{r}", "#{others}", r.spanned_by?(others), "#{r.gaps(others)}", "#{r.overlaps(others)}"]
|
|
763
|
+
end
|
|
764
|
+
result
|
|
765
|
+
#+end_src
|
|
766
|
+
|
|
767
|
+
#+begin_example
|
|
768
|
+
| Self | Others | Spanned By? | Gaps | Overlaps |
|
|
769
|
+
|-------------+-------------------------------------+-------------+--------------+----------|
|
|
770
|
+
| 0..10 | [-1..5, 6..10] | true | [] | [] |
|
|
771
|
+
| 1..20 | [0..10, 11..20] | true | [] | [] |
|
|
772
|
+
| 1..20 | [0..15, 11..20] | false | [] | [11..15] |
|
|
773
|
+
| 1..20 | [0..10, 10..20] | false | [] | [] |
|
|
774
|
+
| 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.5..13.5] | true | [] | [] |
|
|
775
|
+
| 3.145..12.3 | [0.5..3.45, 3.45..10.5, 10.6..13.5] | false | [10.5..10.6] | [] |
|
|
776
|
+
| a..z | ["a".."g", "h".."s", "t".."z"] | true | [] | [] |
|
|
777
|
+
| a..z | ["a".."g", "j".."s", "t".."z"] | false | ["h".."i"] | [] |
|
|
778
|
+
#+end_example
|
|
779
|
+
|
|
780
|
+
*** String
|
|
133
781
|
FatCore::String has methods for performing matching of one string with another
|
|
134
782
|
(~#matches_with~, ~#fuzzy_match~), for converting a string to title-case as
|
|
135
783
|
might by used in the title of a book (~#entitle~), for converting a String
|
|
@@ -138,21 +786,333 @@ into a useable Symbol (~#as_sym~) and vice-versa (~#as_str~ also
|
|
|
138
786
|
cleaning up errant spaces (~#clean~), and computing the Damerau-Levenshtein
|
|
139
787
|
distance between strings (~#distance~). And several others.
|
|
140
788
|
|
|
789
|
+
**** Method ~#fuzzy_match~
|
|
790
|
+
The ~#fuzzy_match~ method determines whether the subject string matches the
|
|
791
|
+
given "matcher" string, which provides a simple syntax that allows a limited
|
|
792
|
+
kind of pattern matching. If there is a match, it returns the matched portion
|
|
793
|
+
of self, minus punctuation characters, if self matches the string, and returns
|
|
794
|
+
nil otherwise.
|
|
795
|
+
|
|
796
|
+
What makes this handy is that a user trying to match by memory can be loose
|
|
797
|
+
about case, punctuation, and spaces, and still find desired matches. In the
|
|
798
|
+
matcher both the space and colon ':' have special meaning as shown below.
|
|
799
|
+
|
|
800
|
+
~#fuzzy_match(matcher)~ uses the following rules for matching:
|
|
801
|
+
|
|
802
|
+
1. Remove leading and trailing whitespace in the subject and the matcher
|
|
803
|
+
and collapse its internal whitespace to a single space,
|
|
804
|
+
2. Remove all periods, commas, apostrophes, and asterisks (the
|
|
805
|
+
punctuation characters) from both self and `matcher`,
|
|
806
|
+
3. Treat internal ':stuff' or ' :stuff' in the matcher as the equivalent
|
|
807
|
+
of /\bstuff.*/ in a regular expression, that is, match any word
|
|
808
|
+
starting with stuff in self,
|
|
809
|
+
4. Treat internal 'stuff: ' in the matcher as the equivalent of /.*stuff\b/ in
|
|
810
|
+
a regular expression, that is, match any word ending with stuff in self,
|
|
811
|
+
5. A colon with no spaces around it is treated as belonging to the
|
|
812
|
+
following word, requiring it to start with it, so 'some:stuff'
|
|
813
|
+
requires 'some' anywhere followed by a word beginning with 'stuff',
|
|
814
|
+
i.e., /some.*\bstuff/i,
|
|
815
|
+
6. Treat leading ':' in the matcher as anchoring the match to the
|
|
816
|
+
beginning of the target string,
|
|
817
|
+
7. Treat ending ':' in the matcher as anchoring the match to the
|
|
818
|
+
end of the target string,
|
|
819
|
+
8. Require each component to match some part of self, and
|
|
820
|
+
|
|
821
|
+
#+begin_src ruby
|
|
822
|
+
require_relative './lib/fat_core/string'
|
|
823
|
+
|
|
824
|
+
result = []
|
|
825
|
+
result << ['Self', 'Matcher', 'Match']
|
|
826
|
+
result << nil
|
|
827
|
+
subj = "St. Luke's Hospital"
|
|
828
|
+
matchers = ['st lukes', 'luk:hosp', 'st:spital', 'uk spital', 'st:laks', ':lukes', 's lukes', 'lukes:hospital']
|
|
829
|
+
matchers.each do |m|
|
|
830
|
+
result << [subj, m, subj.fuzzy_match(m)]
|
|
831
|
+
end
|
|
832
|
+
result
|
|
833
|
+
#+end_src
|
|
834
|
+
|
|
835
|
+
#+begin_example
|
|
836
|
+
| Self | Matcher | Match |
|
|
837
|
+
|---------------------+----------------+----------------|
|
|
838
|
+
| St. Luke's Hospital | st lukes | St Lukes |
|
|
839
|
+
| St. Luke's Hospital | luk:hosp | Lukes Hosp |
|
|
840
|
+
| St. Luke's Hospital | st:spital | nil |
|
|
841
|
+
| St. Luke's Hospital | uk spital | ukes Hospital |
|
|
842
|
+
| St. Luke's Hospital | st:laks | nil |
|
|
843
|
+
| St. Luke's Hospital | :lukes | nil |
|
|
844
|
+
| St. Luke's Hospital | s lukes | St Lukes |
|
|
845
|
+
| St. Luke's Hospital | lukes:hospital | Lukes Hospital |
|
|
846
|
+
#+end_example
|
|
847
|
+
|
|
848
|
+
**** Method ~#matches_with~
|
|
849
|
+
The ~#matches_with(matcher)~ method allows the use of either a regular
|
|
850
|
+
expression or fuzzy matching as described above depending on whether the
|
|
851
|
+
matcher is enclosed in '/' characters. It also returns the matched portion of
|
|
852
|
+
~self~ or nil if there is no match. Even when a regex is given, the match is
|
|
853
|
+
case insensitive by default and commas, apostrophes, and periods are removed
|
|
854
|
+
from the subject string before matching.
|
|
855
|
+
|
|
856
|
+
#+begin_src ruby
|
|
857
|
+
require_relative './lib/fat_core/string'
|
|
858
|
+
|
|
859
|
+
result = []
|
|
860
|
+
result << ['Self', 'Matcher', 'Match']
|
|
861
|
+
result << nil
|
|
862
|
+
subj = "St. Luke's Hospital"
|
|
863
|
+
matchers = ['st lukes', '/luk.*hosp/', 'st:spital', '/u.*s\b/', 'st:laks', ':lukes', 's lukes', '/lukes hospital\z/']
|
|
864
|
+
matchers.each do |m|
|
|
865
|
+
result << [subj, m, subj.matches_with(m)]
|
|
866
|
+
end
|
|
867
|
+
result
|
|
868
|
+
#+end_src
|
|
869
|
+
|
|
870
|
+
#+begin_example
|
|
871
|
+
| Self | Matcher | Match |
|
|
872
|
+
|---------------------+--------------------+----------------|
|
|
873
|
+
| St. Luke's Hospital | st lukes | St Lukes |
|
|
874
|
+
| St. Luke's Hospital | /luk.*hosp/ | Lukes Hosp |
|
|
875
|
+
| St. Luke's Hospital | st:spital | nil |
|
|
876
|
+
| St. Luke's Hospital | /u.*s\b/ | ukes |
|
|
877
|
+
| St. Luke's Hospital | st:laks | nil |
|
|
878
|
+
| St. Luke's Hospital | :lukes | nil |
|
|
879
|
+
| St. Luke's Hospital | s lukes | St Lukes |
|
|
880
|
+
| St. Luke's Hospital | /lukes hospital\z/ | Lukes Hospital |
|
|
881
|
+
#+end_example
|
|
882
|
+
|
|
883
|
+
**** Method ~#entitle~
|
|
884
|
+
For a string meant to serve as the title of a book, song, or other item, there
|
|
885
|
+
are certain rules in English as to which words should be capitalized and which
|
|
886
|
+
should be put in lower case. "PROFILES IN courage" should be rendered
|
|
887
|
+
"Profiles in Courage" for example. The preposition "in" is typically not
|
|
888
|
+
capitalized unless it starts the title: "in the HEAT OF THE NIght" should be
|
|
889
|
+
something like "In the Heat of the Night".
|
|
890
|
+
|
|
891
|
+
#+begin_src ruby
|
|
892
|
+
require_relative './lib/fat_core/string'
|
|
893
|
+
|
|
894
|
+
result = []
|
|
895
|
+
result << ['Self', 'Entitled']
|
|
896
|
+
result << nil
|
|
897
|
+
titles = ['PROFILES IN courage', 'in the HEAT OF THE NIght', 'a day in the life', 'FROM HERE TO ETERNITY',
|
|
898
|
+
'lucy in the sky with diamonds']
|
|
899
|
+
titles.each do |t|
|
|
900
|
+
result << [t, t.entitle]
|
|
901
|
+
end
|
|
902
|
+
result
|
|
903
|
+
#+end_src
|
|
904
|
+
|
|
905
|
+
#+begin_example
|
|
906
|
+
| Self | Entitled |
|
|
907
|
+
|-------------------------------+-------------------------------|
|
|
908
|
+
| PROFILES IN courage | Profiles in Courage |
|
|
909
|
+
| in the HEAT OF THE NIght | In the Heat of the Night |
|
|
910
|
+
| a day in the life | A Day in the Life |
|
|
911
|
+
| FROM HERE TO ETERNITY | From Here to Eternity |
|
|
912
|
+
| lucy in the sky with diamonds | Lucy in the Sky With Diamonds |
|
|
913
|
+
#+end_example
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
**** Method ~#distance~
|
|
917
|
+
~FatCore~ provides ~distance~ as a simple wrapper around the
|
|
918
|
+
Damerau-Levenshtein distance function in ~damerau-levenshtein~ gem, using a
|
|
919
|
+
block size of 1 and a max distance of 10.
|
|
920
|
+
|
|
921
|
+
#+begin_src ruby
|
|
922
|
+
require_relative './lib/fat_core/string'
|
|
923
|
+
|
|
924
|
+
result = []
|
|
925
|
+
result << ['Word1', 'Word2', 'Distance']
|
|
926
|
+
result << nil
|
|
927
|
+
pairs = [['Shelf', 'Shell'], ['Shelf', 'Shall'], ['Doherty', 'Daughtery'], ['Doherty', 'Dorrit'], ['Smith', 'Jones']]
|
|
928
|
+
pairs.each do |w1, w2|
|
|
929
|
+
result << [w1, w2, w1.distance(w2)]
|
|
930
|
+
end
|
|
931
|
+
result
|
|
932
|
+
#+end_src
|
|
933
|
+
|
|
934
|
+
#+begin_example
|
|
935
|
+
| Word1 | Word2 | Distance |
|
|
936
|
+
|---------+-----------+----------|
|
|
937
|
+
| Shelf | Shell | 1 |
|
|
938
|
+
| Shelf | Shall | 2 |
|
|
939
|
+
| Doherty | Daughtery | 5 |
|
|
940
|
+
| Doherty | Dorrit | 4 |
|
|
941
|
+
| Smith | Jones | 5 |
|
|
942
|
+
#+end_example
|
|
943
|
+
|
|
944
|
+
**** Method ~#commas(places)~
|
|
945
|
+
When presenting numbers, it is common to want to add grouping digits to make
|
|
946
|
+
the numbers more readable. The ~commas(places)~ method does this be
|
|
947
|
+
converting the number into a Float, rounding to places digits, then converting
|
|
948
|
+
back to a ~String~ with grouping commas inserted.
|
|
949
|
+
|
|
950
|
+
#+begin_src ruby
|
|
951
|
+
require_relative './lib/fat_core/string'
|
|
952
|
+
|
|
953
|
+
result = []
|
|
954
|
+
result << ['N', 'Places', 'With Commas']
|
|
955
|
+
result << nil
|
|
956
|
+
nums_places = [["798964655.66541325", 3], ["798964655.66541325", 0], ["798964655.66541325", 5], ["3.14159", 3],
|
|
957
|
+
["3.14159e6", 3], ["-3.14159e4", 2], ["+3.14159e3", 2]]
|
|
958
|
+
nums_places.each do |n, p|
|
|
959
|
+
result << [n, p, n.commas(p)]
|
|
960
|
+
end
|
|
961
|
+
result
|
|
962
|
+
#+end_src
|
|
963
|
+
|
|
964
|
+
#+begin_example
|
|
965
|
+
| N | Places | With Commas |
|
|
966
|
+
|--------------------+--------+-------------------|
|
|
967
|
+
| 798964655.66541325 | 3 | 798,964,655.665 |
|
|
968
|
+
| 798964655.66541325 | 0 | 798,964,656 |
|
|
969
|
+
| 798964655.66541325 | 5 | 798,964,655.66541 |
|
|
970
|
+
| 3.14159 | 3 | 3.142 |
|
|
971
|
+
| 3.14159e6 | 3 | 3,141,590.000 |
|
|
972
|
+
| -3.14159e4 | 2 | -31,415.90 |
|
|
973
|
+
| +3.14159e3 | 2 | 3,141.59 |
|
|
974
|
+
#+end_example
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
**** Method =#wrap(width, hang)=
|
|
978
|
+
This method wraps the string to a given width with an optional hanging indent
|
|
979
|
+
for lines after the first.
|
|
980
|
+
#+begin_src ruby
|
|
981
|
+
require_relative './lib/fat_core/string'
|
|
982
|
+
|
|
983
|
+
getty = <<~EOS
|
|
984
|
+
Four score and seven years ago our fathers brought forth on this continent,
|
|
985
|
+
a new nation, conceived in Liberty, and dedicated to the proposition that
|
|
986
|
+
all men are created equal.
|
|
987
|
+
|
|
988
|
+
Now we are engaged in a great civil war, testing whether that nation, or any
|
|
989
|
+
nation so conceived and so dedicated, can long endure. We are met on a
|
|
990
|
+
great battle-field of that war. We have come to dedicate a portion of that
|
|
991
|
+
field, as a final resting place for those who here gave their lives that
|
|
992
|
+
that nation might live. It is altogether fitting and proper that we should
|
|
993
|
+
do this.
|
|
994
|
+
|
|
995
|
+
But, in a larger sense, we can not dedicate---we can not consecrate---we can
|
|
996
|
+
not hallow---this ground. The brave men, living and dead, who struggled
|
|
997
|
+
here, have consecrated it, far above our poor power to add or detract. The
|
|
998
|
+
world will little note, nor long remember what we say here, but it can never
|
|
999
|
+
forget what they did here. It is for us the living, rather, to be dedicated
|
|
1000
|
+
here to the unfinished work which they who fought here have thus far so
|
|
1001
|
+
nobly advanced. It is rather for us to be here dedicated to the great task
|
|
1002
|
+
remaining before us---that from these honored dead we take increased
|
|
1003
|
+
devotion to that cause for which they gave the last full measure of
|
|
1004
|
+
devotion---that we here highly resolve that these dead shall not have died
|
|
1005
|
+
in vain---that this nation, under God, shall have a new birth of
|
|
1006
|
+
freedom---and that government of the people, by the people, for the people,
|
|
1007
|
+
shall not perish from the earth.
|
|
1008
|
+
EOS
|
|
1009
|
+
getty.wrap(110, 3)
|
|
1010
|
+
#+end_src
|
|
1011
|
+
|
|
1012
|
+
#+begin_example
|
|
1013
|
+
Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived
|
|
1014
|
+
in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged
|
|
1015
|
+
in a great civil war, testing whether that nation, or any nation so conceived and so dedicated,
|
|
1016
|
+
can long endure. We are met on a great battle-field of that war. We have come to dedicate
|
|
1017
|
+
a portion of that field, as a final resting place for those who here gave their lives that
|
|
1018
|
+
that nation might live. It is altogether fitting and proper that we should do this. But, in
|
|
1019
|
+
a larger sense, we can not dedicate---we can not consecrate---we can not hallow---this ground.
|
|
1020
|
+
The brave men, living and dead, who struggled here, have consecrated it, far above our poor
|
|
1021
|
+
power to add or detract. The world will little note, nor long remember what we say here, but
|
|
1022
|
+
it can never forget what they did here. It is for us the living, rather, to be dedicated here
|
|
1023
|
+
to the unfinished work which they who fought here have thus far so nobly advanced. It is rather
|
|
1024
|
+
for us to be here dedicated to the great task remaining before us---that from these honored
|
|
1025
|
+
dead we take increased devotion to that cause for which they gave the last full measure of
|
|
1026
|
+
devotion---that we here highly resolve that these dead shall not have died in vain---that this
|
|
1027
|
+
nation, under God, shall have a new birth of freedom---and that government of the people, by
|
|
1028
|
+
the people, for the people, shall not perish from the earth.
|
|
1029
|
+
#+end_example
|
|
1030
|
+
|
|
1031
|
+
**** Method =#as_sym=
|
|
1032
|
+
Convert a ~String~ to a ~Symbol~ by converting all letters to lower-case,
|
|
1033
|
+
replacing hyphens and white space with a single underscore, and removing all
|
|
1034
|
+
non-alphanumeric characters:
|
|
1035
|
+
|
|
1036
|
+
#+begin_src ruby
|
|
1037
|
+
require_relative './lib/fat_core/string'
|
|
1038
|
+
|
|
1039
|
+
" Hello-to-the World!!!".as_sym
|
|
1040
|
+
#+end_src
|
|
1041
|
+
|
|
1042
|
+
#+begin_example
|
|
1043
|
+
:hello_to_the_world
|
|
1044
|
+
#+end_example
|
|
1045
|
+
|
|
1046
|
+
*** Symbol
|
|
1047
|
+
**** Method =#as_str=
|
|
1048
|
+
A quasi-inverse of ~String#as_sym~, convert a ~Symbol~ into a ~String~ by
|
|
1049
|
+
converting '_' to a hyphen, white-space into '_', and eliminate any
|
|
1050
|
+
non-alphanumeric characters.
|
|
1051
|
+
|
|
1052
|
+
#+begin_src ruby
|
|
1053
|
+
require_relative 'lib/fat_core/symbol'
|
|
1054
|
+
|
|
1055
|
+
:hello_to_the_world.as_str
|
|
1056
|
+
#+end_src
|
|
1057
|
+
|
|
1058
|
+
#+begin_example
|
|
1059
|
+
hello-to-the-world
|
|
1060
|
+
#+end_example
|
|
1061
|
+
|
|
141
1062
|
*** TeX Quoting
|
|
1063
|
+
The extensions for ~String~, ~Numeric~, ~Range~, ~Symbol~, and ~NilClass~
|
|
1064
|
+
provide a ~#tex_quote~ method for quoting the string version of an object so
|
|
1065
|
+
as to allow its inclusion in a TeX document while quoting characters such as
|
|
1066
|
+
'_', $' or '%' that have a special meaning for TeX. At the same time it
|
|
1067
|
+
deploys TeX notation when special notation is available, for example, a
|
|
1068
|
+
~Rational~ is rendered as a fraction.
|
|
142
1069
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
have a special meaning for TeX.
|
|
1070
|
+
#+begin_src ruby
|
|
1071
|
+
require_relative 'lib/fat_core/all'
|
|
1072
|
+
require 'date'
|
|
147
1073
|
|
|
148
|
-
|
|
1074
|
+
result = []
|
|
1075
|
+
result << ['Class', 'Example', '#tex_quote']
|
|
1076
|
+
result << nil
|
|
1077
|
+
examples = [
|
|
1078
|
+
"Save $100 or 14% on this Friday_Black",
|
|
1079
|
+
58743.44,
|
|
1080
|
+
Float::INFINITY,
|
|
1081
|
+
Math::PI,
|
|
1082
|
+
Complex(5, 3),
|
|
1083
|
+
Complex(5.0, 3.0),
|
|
1084
|
+
Rational(5, 3),
|
|
1085
|
+
Rational(8.0, 17.0),
|
|
1086
|
+
(Date.parse('2020-09-22')..Date.today),
|
|
1087
|
+
(Math::E..Math::PI),
|
|
1088
|
+
:four_score_and_7_years,
|
|
1089
|
+
nil
|
|
1090
|
+
]
|
|
1091
|
+
examples.each do |ex|
|
|
1092
|
+
result << [ex.class, ex.to_s, ex.tex_quote.inspect]
|
|
1093
|
+
end
|
|
1094
|
+
result
|
|
1095
|
+
#+end_src
|
|
1096
|
+
|
|
1097
|
+
#+begin_example
|
|
1098
|
+
| Class | Example | #tex_quote |
|
|
1099
|
+
|----------+---------------------------------------+-----------------------------------------------|
|
|
1100
|
+
| String | Save $100 or 14% on this Friday_Black | "Save \\$100 or 14\\% on this Friday\\_Black" |
|
|
1101
|
+
| Float | 58743.44 | "58743.44" |
|
|
1102
|
+
| Float | Infinity | "$\\infty$" |
|
|
1103
|
+
| Float | 3.141592653589793 | "$\\pi$" |
|
|
1104
|
+
| Complex | 5+3i | "$5+3i$" |
|
|
1105
|
+
| Complex | 5.0+3.0i | "$5+3i$" |
|
|
1106
|
+
| Rational | 5/3 | "$\\frac{5}{3}$" |
|
|
1107
|
+
| Rational | 8/17 | "$\\frac{8}{17}$" |
|
|
1108
|
+
| Range | 2020-09-22..2025-11-24 | "(2020-09-22..2025-11-24)" |
|
|
1109
|
+
| Range | 2.718281828459045..3.141592653589793 | "($e$..$\\pi$)" |
|
|
1110
|
+
| Symbol | four_score_and_7_years | "four\\_score\\_and\\_7\\_years" |
|
|
1111
|
+
| NilClass | | "" |
|
|
1112
|
+
#+end_example
|
|
149
1113
|
|
|
150
|
-
FatCore::Numeric has methods for inserting grouping commas into a number
|
|
151
|
-
(~#commas~ and ~#group~), for converting seconds to HH:MM:SS.dd format
|
|
152
|
-
(~#secs_to_hms~), for testing for integrality (~#whole?~ and ~#int_if_whole~), and
|
|
153
|
-
testing for sign (~#signum~).
|
|
154
1114
|
|
|
155
|
-
|
|
1115
|
+
* Contributing
|
|
156
1116
|
|
|
157
1117
|
1. Fork it ([[http://github.com/ddoherty03/fat_core/fork]] )
|
|
158
1118
|
2. Create your feature branch (~git checkout -b my-new-feature~)
|