xqsr3 0.31.2 → 0.32.2

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
  SHA1:
3
- metadata.gz: 7ee411febd1aacd76190a4d3032cd5a8ed0e5376
4
- data.tar.gz: 4fe4ff7827e9c0aa08c2e036a02db5c1ea197601
3
+ metadata.gz: d08b7ea53bfd21226d79bb36506890a7868ba93b
4
+ data.tar.gz: c4d23e493010555d8c3cfb27c515b469a47dd29f
5
5
  SHA512:
6
- metadata.gz: fbb6ff8feb168e0c7f28d1d32fccd4933dd96943c583f897a58e40a31b7bcd3cee62734e6f08a8471901f151c4a1a5cbcf8e730377f36579d42f11573d6210ed
7
- data.tar.gz: 567578a5041f37710becf55c78857526e88e0c7720c9c8be3fe82c7da366a57521b1d2642e5c31443d676e9261695a8d1b2d2599496a927772c7bc56ac7d5d54
6
+ metadata.gz: 34a1d293c2de8559801ec22e038a4e7fc33a7d6da5901411d84f1aa6ca67cd22295f690ecc501b166e9aa8777f5b00408473b467782ff129ebade29e0a0e15e0
7
+ data.tar.gz: 9e9a153905629a84437e69ea297ca7bbc843542a3ab90b35997d1e79d816ded0d90d2c2e8381c9db13ca6ffe6a4a8ae0691c2db5a7b4478a58efef4dc2e52ee4
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  xqsr3
2
2
 
3
- Copyright (c) 2005-2016, Matthew Wilson and Synesis Software
3
+ Copyright (c) 2005-2019, Matthew Wilson and Synesis Software
4
4
  All rights reserved.
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without
@@ -27,3 +27,4 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
27
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
28
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
29
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+
data/README.md CHANGED
@@ -20,7 +20,29 @@ It may be pronounced (lamely) as "excusers".
20
20
 
21
21
  ## Installation
22
22
 
23
- Install using `gem install xqsr3` or add it to your `Gemfile`.
23
+ Install via **gem** as in:
24
+
25
+ ```
26
+ gem install libclimate-ruby
27
+ ```
28
+
29
+ or add it to your `Gemfile`.
30
+
31
+ Use is via specific APIs or groups. For example, in order to use the
32
+ ``FrequencyMap`` class you would ``require`` the source file, as in:
33
+
34
+ ```Ruby
35
+ require 'xqsr3/containers/frequency_map'
36
+ ```
37
+
38
+ Alternatively, to use all **test/unit** extensions you would ``require`` all
39
+ relatived via the file:
40
+
41
+ ```Ruby
42
+ require 'xqsr3/extensions/test/unit'
43
+ ```
44
+
45
+ which brings in nine extensions.
24
46
 
25
47
  ## Components
26
48
 
@@ -49,6 +71,10 @@ and extensions to the following standard library components:
49
71
  * String extensions
50
72
  * test/unit extensions
51
73
 
74
+ ## Examples
75
+
76
+ Examples are provided in the ```examples``` directory, along with a markdown description for each. A detailed list TOC of them is provided in [EXAMPLES.md](./EXAMPLES.md).
77
+
52
78
  ## Project Information
53
79
 
54
80
  ### Where to get help
@@ -61,17 +87,20 @@ Defect reports, feature requests, and pull requests are welcome on https://githu
61
87
 
62
88
  ### Related projects
63
89
 
64
- **xqsr3** is a runtime dependency in:
90
+ **xqsr3** is a runtime dependency of:
65
91
 
66
92
  * the **[libCLImate.Ruby](https://github.com/synesissoftware/libCLImate.Ruby)** library;
67
93
  * the [**xqsr3-xml**](https://github.com/synesissoftware.com/xqsr3-xml/) library.
68
94
 
69
- and a development dependency in:
95
+ and a development dependency of:
70
96
 
71
97
  * the **[CLASP.Ruby](https://github.com/synesissoftware/CLASP.Ruby)** library;
72
98
  * the **[cmpfs.Ruby](https://github.com/synesissoftware/cmpfs.Ruby)** library;
99
+ * the **[libpath.Ruby](https://github.com/synesissoftware/libpath.Ruby)** library;
73
100
  * the **[Pantheios.Ruby](https://github.com/synesissoftware/Pantheios.Ruby)** library.
101
+ * the **[Quench.Ruby](https://github.com/synesissoftware/Quench.Ruby)** library.
74
102
 
75
103
  ### License
76
104
 
77
105
  **xqsr3** is released under the 3-clause BSD license. See [LICENSE](./LICENSE) for details.
106
+
@@ -0,0 +1,82 @@
1
+ # xqsr3 Example - **count_word_frequencies**
2
+
3
+ ## Summary
4
+
5
+ Simple example illustrating use of ``FrequencyMap`` class.
6
+
7
+ ## Source
8
+
9
+ ```ruby
10
+
11
+ #!/usr/bin/env ruby
12
+
13
+ # examples/count_word_frequencies.rb
14
+
15
+ require 'xqsr3/containers/frequency_map'
16
+
17
+ include Xqsr3::Containers
18
+
19
+ puts "Analyse DATA words via FrequencyMap"
20
+ puts
21
+
22
+ data = DATA.read
23
+ data = data.gsub(/\n/, ' ')
24
+ words = data.split
25
+
26
+
27
+ puts "1. Manually (showing items with 2+ occurrences):"
28
+
29
+ fm = FrequencyMap.new
30
+
31
+ words.each { |word| fm << word }
32
+
33
+ fm.each_by_frequency do |word, frequency|
34
+
35
+ next if 1 == frequency
36
+
37
+ $stdout.puts "\t#{word}\t#{frequency}"
38
+ end
39
+ puts
40
+
41
+
42
+ puts "2. Via ByElement (showing items with 2+ occurrences):"
43
+
44
+ fm = FrequencyMap::ByElement[*words]
45
+
46
+ fm.each_by_frequency do |word, frequency|
47
+
48
+ next if 1 == frequency
49
+
50
+ $stdout.puts "\t#{word}\t#{frequency}"
51
+ end
52
+ puts
53
+
54
+
55
+
56
+ __END__
57
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
58
+ ```
59
+
60
+ The code is pretty self-explanatory. I've grabbed some **Lorem Ipsum** and placed into an ``__END__`` section, which is then read (via ``DATA``) and ``split`` into ``words``. These words are then pushed into ``FrequencyMap`` instances in two ways: manually; and via ``FrequencyMap::ByElement``. Each list is written out, omitting single occurrences, inside an ``each_by_frequency`` loop.
61
+
62
+ ## Usage
63
+
64
+ When run, this produces the following output:
65
+
66
+ ```
67
+ Analyse DATA words via FrequencyMap
68
+
69
+ 1. Manually (showing items with 2+ occurrences):
70
+ in 3
71
+ dolor 2
72
+ dolore 2
73
+ ut 2
74
+
75
+ 2. Via ByElement (showing items with 2+ occurrences):
76
+ in 3
77
+ dolor 2
78
+ dolore 2
79
+ ut 2
80
+ ```
81
+
82
+
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # examples/count_word_frequencies.rb
4
+
5
+ require 'xqsr3/containers/frequency_map'
6
+
7
+ include Xqsr3::Containers
8
+
9
+
10
+ puts "Analyse DATA words via FrequencyMap"
11
+ puts
12
+
13
+ data = DATA.read
14
+ data = data.gsub(/\n/, ' ')
15
+ words = data.split
16
+
17
+
18
+ puts "1. Manually (showing items with 2+ occurrences):"
19
+
20
+ fm = FrequencyMap.new
21
+
22
+ words.each { |word| fm << word }
23
+
24
+ fm.each_by_frequency do |word, frequency|
25
+
26
+ next if 1 == frequency
27
+
28
+ $stdout.puts "\t#{word}\t#{frequency}"
29
+ end
30
+ puts
31
+
32
+
33
+ puts "2. Via ByElement (showing items with 2+ occurrences):"
34
+
35
+ fm = FrequencyMap::ByElement[*words]
36
+
37
+ fm.each_by_frequency do |word, frequency|
38
+
39
+ next if 1 == frequency
40
+
41
+ $stdout.puts "\t#{word}\t#{frequency}"
42
+ end
43
+ puts
44
+
45
+
46
+
47
+ __END__
48
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
49
+
@@ -6,7 +6,7 @@
6
6
  # module
7
7
  #
8
8
  # Created: 7th December 2017
9
- # Updated: 10th April 2019
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
@@ -64,7 +64,7 @@ module JoinWithOr
64
64
  #
65
65
  # === Signature
66
66
  #
67
- # * *Parameters*
67
+ # * *Parameters:*
68
68
  #
69
69
  # * *Required parameters*:
70
70
  # - +ar+:: [Array] The array whose contents are to be joined
@@ -73,7 +73,7 @@ module JoinWithOr
73
73
  # - +options+:: [Hash] Options that control the behaviour of the
74
74
  # method
75
75
  #
76
- # * *Options*:
76
+ # * *Options:*
77
77
  #
78
78
  # - +:or+:: [String] A string that is used instead of 'or'
79
79
  # - +:oxford_comma+:: [boolean] Determines whether an Oxford comma
@@ -5,13 +5,13 @@
5
5
  # Purpose: FrequencyMap container
6
6
  #
7
7
  # Created: 28th January 2005
8
- # Updated: 13th October 2018
8
+ # Updated: 12th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2005-2018, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2005-2019, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -44,8 +44,7 @@
44
44
  # ######################################################################## #
45
45
 
46
46
 
47
- # ##########################################################
48
- # ::Xqsr3::Containers::FrequencyMap
47
+ require 'xqsr3/diagnostics/inspect_builder'
49
48
 
50
49
  =begin
51
50
  =end
@@ -53,10 +52,29 @@
53
52
  module Xqsr3
54
53
  module Containers
55
54
 
55
+ # Hash-like class that counts, as the map's values, the frequencies of
56
+ # elements, as the map's keys
56
57
  class FrequencyMap
57
58
 
58
59
  include Enumerable
60
+ include ::Xqsr3::Diagnostics::InspectBuilder
59
61
 
62
+ # Class that provides a Hash[]-like syntax as follows:
63
+ #
64
+ # fm = FrequencyMap::ByElement[ 'abc', 'def', 'abc', :x, 'x', :y ]
65
+ #
66
+ # fm.empty? # => false
67
+ # fm.size # => 5
68
+ # fm.count # => 6
69
+ # fm['abc'] # => 2
70
+ # fm['def'] # => 1
71
+ # fm['ghi'] # => 0
72
+ # fm['x'] # => 1
73
+ # fm['y'] # => 0
74
+ # fm['z'] # => 0
75
+ # fm[:x] # => 1
76
+ # fm[:y] # => 1
77
+ # fm[:z] # => 0
60
78
  ByElement = Class.new do
61
79
 
62
80
  def self.[] *args
@@ -71,6 +89,7 @@ class FrequencyMap
71
89
  private_class_method :new
72
90
  end
73
91
 
92
+ # Creates an instance from the given arguments
74
93
  def self.[] *args
75
94
 
76
95
  return self.new if 0 == args.length
@@ -134,26 +153,38 @@ class FrequencyMap
134
153
  end
135
154
  end
136
155
 
156
+ # Initialises an instance
137
157
  def initialize
138
158
 
139
- @counts = {}
159
+ @elements = {}
140
160
  @count = 0
141
161
  end
142
162
 
163
+ # Pushes an element into the map, assigning it an initial count of 1
164
+ #
165
+ # * *Parameters:*
166
+ # - +key+:: The element to insert
143
167
  def << key
144
168
 
145
169
  push key, 1
146
170
  end
147
171
 
172
+ # Compares the instance for equality against +rhs+
173
+ #
174
+ # * *Parameters:*
175
+ # - +rhs+ (+nil+, +::Hash+, +FrequencyMap+) The instance to compare against
176
+ #
177
+ # * *Exceptions:*
178
+ # - +::TypeError+ if +rhs+ is not of the required type(s)
148
179
  def == rhs
149
180
 
150
181
  case rhs
151
182
  when ::NilClass
152
183
  return false
153
184
  when ::Hash
154
- return rhs.size == @counts.size && rhs == @counts
185
+ return rhs.size == @elements.size && rhs == @elements
155
186
  when self.class
156
- return rhs.count == self.count && rhs == @counts
187
+ return rhs.count == self.count && rhs == @elements
157
188
  else
158
189
  raise TypeError, "can compare #{self.class} only to instances of #{self.class} and #{::Hash}, but #{rhs.class} given"
159
190
  end
@@ -161,24 +192,41 @@ class FrequencyMap
161
192
  false
162
193
  end
163
194
 
195
+ # Obtains the count for a given key, or +nil+ if the key does not exist
196
+ #
197
+ # * *Parameters:*
198
+ # - +key+ The key to lookup
164
199
  def [] key
165
200
 
166
- @counts[key]
201
+ @elements[key] || 0
167
202
  end
168
203
 
204
+ # Assigns a key and a count
205
+ #
206
+ # * *Parameters:*
207
+ # - +key+ The key to lookup
208
+ # - +count+ (::Integer) The count to lookup
209
+ #
210
+ # * *Exceptions:*
211
+ # - +::TypeError+ if +count+ is not an +::Integer+
169
212
  def []= key, count
170
213
 
214
+ raise TypeError, "'count' parameter must be of type #{::Integer}, but was of type #{count.class}" unless Integer === count
215
+
171
216
  store key, count
172
217
  end
173
218
 
219
+ # Searches the instance comparing each element with +key+, returning the
220
+ # count if found, or +nil+ if not
174
221
  def assoc key
175
222
 
176
- @counts.assoc key
223
+ @elements.assoc key
177
224
  end
178
225
 
226
+ # Removes all elements from the instance
179
227
  def clear
180
228
 
181
- @counts.clear
229
+ @elements.clear
182
230
  @count = 0
183
231
  end
184
232
 
@@ -188,18 +236,24 @@ class FrequencyMap
188
236
  @count
189
237
  end
190
238
 
239
+ # Obtains the default value of the instance, which will always be +nil+
191
240
  def default
192
241
 
193
- @counts.default
242
+ @elements.default
194
243
  end
195
244
 
245
+ # Deletes the element with the given +key+ and its counts
246
+ #
247
+ # * *Parameters:*
248
+ # - +key+ The key to delete
196
249
  def delete key
197
250
 
198
- key_count = @counts.delete key
251
+ key_count = @elements.delete key
199
252
 
200
253
  @count -= key_count if key_count
201
254
  end
202
255
 
256
+ # Duplicates the instance
203
257
  def dup
204
258
 
205
259
  fm = self.class.new
@@ -207,9 +261,14 @@ class FrequencyMap
207
261
  fm.merge! self
208
262
  end
209
263
 
264
+ # Calls _block_ once for each element in the instance, passing the element
265
+ # and its frequency as parameters. If no block is provided, an enumerator
266
+ # is returned
210
267
  def each
211
268
 
212
- @counts.each do |k, v|
269
+ return @elements.each unless block_given?
270
+
271
+ @elements.each do |k, v|
213
272
 
214
273
  yield k, v
215
274
  end
@@ -221,9 +280,9 @@ class FrequencyMap
221
280
  # keys must be created and sorted from which enumeration is directed
222
281
  def each_by_key
223
282
 
224
- @counts.keys.sort.each do |key|
283
+ @elements.keys.sort.each do |key|
225
284
 
226
- yield key, @counts[key]
285
+ yield key, @elements[key]
227
286
  end
228
287
  end
229
288
 
@@ -235,7 +294,7 @@ class FrequencyMap
235
294
  def each_by_frequency
236
295
 
237
296
  tm = {}
238
- @counts.each do |element, frequency|
297
+ @elements.each do |element, frequency|
239
298
 
240
299
  tm[frequency] = [] unless tm.has_key?(frequency)
241
300
 
@@ -253,8 +312,12 @@ class FrequencyMap
253
312
  end
254
313
  end
255
314
 
315
+ # Calls _block_ once for each element in the instance, passing the
316
+ # element. If no block is provided, an enumerator is returned
256
317
  def each_key
257
318
 
319
+ return @elements.each_key unless block_given?
320
+
258
321
  keys.each do |element|
259
322
 
260
323
  yield element
@@ -263,19 +326,26 @@ class FrequencyMap
263
326
 
264
327
  alias each_pair each
265
328
 
329
+ # Calls _block_ once for each element in the instance, passing the
330
+ # count. If no block is provided, an enumerator is returned
266
331
  def each_value
267
332
 
333
+ return @elements.each_value unless block_given?
334
+
268
335
  keys.each do |element|
269
336
 
270
- yield @counts[element]
337
+ yield @elements[element]
271
338
  end
272
339
  end
273
340
 
341
+ # Returns +true+ if instance contains no elements; +false+ otherwise
274
342
  def empty?
275
343
 
276
344
  0 == size
277
345
  end
278
346
 
347
+ # Returns +true+ if +rhs+ is an instance of +FrequencyMap+ and contains
348
+ # the same elements and their counts; +false+ otherwise
279
349
  def eql? rhs
280
350
 
281
351
  case rhs
@@ -286,6 +356,11 @@ class FrequencyMap
286
356
  end
287
357
  end
288
358
 
359
+ # Returns the count from the instance for the given element +key+. If
360
+ # +key+ cannot be found, there are several options: with no other
361
+ # arguments, it will raise a +::KeyError+ exception; if +default+ is
362
+ # given, then that will be returned; if the optional code block is
363
+ # specified, then that will be run and its result returned
289
364
  def fetch key, default = nil, &block
290
365
 
291
366
  case default
@@ -295,7 +370,7 @@ class FrequencyMap
295
370
  raise TypeError, "default parameter ('#{default}') must be of type #{::Integer}, but was of type #{default.class}"
296
371
  end
297
372
 
298
- unless @counts.has_key? key
373
+ unless @elements.has_key? key
299
374
 
300
375
  return default unless default.nil?
301
376
 
@@ -314,19 +389,24 @@ class FrequencyMap
314
389
  raise KeyError, "given key '#{key}' (#{key.class}) does not exist"
315
390
  end
316
391
 
317
- @counts[key]
392
+ @elements[key]
318
393
  end
319
394
 
395
+ # Returns the equivalent flattened form of the instance
320
396
  def flatten
321
397
 
322
- @counts.flatten
398
+ @elements.flatten
323
399
  end
324
400
 
401
+ # Returns +true+ if an element with the given +key+ is in the map; +false+
402
+ # otherwise
325
403
  def has_key? key
326
404
 
327
- @counts.has_key? key
405
+ @elements.has_key? key
328
406
  end
329
407
 
408
+ # Returns +true+ if an element with a count of the given +value+ is in the
409
+ # map; +false+ otherwise
330
410
  def has_value? value
331
411
 
332
412
  case value
@@ -336,52 +416,63 @@ class FrequencyMap
336
416
  raise TypeError, "parameter ('#{value}') must be of type #{::Integer}, but was of type #{value.class}"
337
417
  end
338
418
 
339
- @counts.has_value? value
419
+ @elements.has_value? value
340
420
  end
341
421
 
422
+ # A hash-code for this instance
342
423
  def hash
343
424
 
344
- @counts.hash
425
+ @elements.hash
345
426
  end
346
427
 
347
428
  alias include? has_key?
348
429
 
430
+ # A diagnostics string form of the instance
349
431
  def inspect
350
432
 
351
- @counts.inspect
433
+ make_inspect show_fields: true
352
434
  end
353
435
 
354
436
  # def keep_if
355
437
  #
356
- # @counts.keep_if
438
+ # @elements.keep_if
357
439
  # end
358
440
 
359
- def key value
441
+ # Returns the element that has the given count, or +nil+ if none found
442
+ #
443
+ # * *Parameters:*
444
+ # - +count+ (::Integer) The count to lookup
445
+ #
446
+ # * *Exceptions:*
447
+ # - +::TypeError+ if +count+ is not of the required type(s)
448
+ def key count
360
449
 
361
- case value
362
- when ::NilClass, ::Integer
363
- ;
364
- else
365
- raise TypeError, "parameter ('#{value}') must be of type #{::Integer}, but was of type #{value.class}"
366
- end
450
+ raise TypeError, "'count' parameter must be of type #{::Integer}, but was of type #{count.class}" unless Integer === count
367
451
 
368
- @counts.key value
452
+ @elements.key count
369
453
  end
370
454
 
371
455
  alias key? has_key?
372
456
 
457
+ # An array of the elements only
373
458
  def keys
374
459
 
375
- @counts.keys
460
+ @elements.keys
376
461
  end
377
462
 
463
+ # The number of elements in the map
378
464
  def length
379
465
 
380
- @counts.length
466
+ @elements.length
381
467
  end
382
468
 
383
469
  alias member? has_key?
384
470
 
471
+ # Returns a new instance containing a merging of the current instance and
472
+ # the +fm+ instance
473
+ #
474
+ # NOTE: where any element is found in both merging instances the count
475
+ # will be a combination of the two counts
385
476
  def merge fm
386
477
 
387
478
  raise TypeError, "parameter must be an instance of type #{self.class}" unless fm.instance_of? self.class
@@ -394,16 +485,20 @@ class FrequencyMap
394
485
  fm_new
395
486
  end
396
487
 
488
+ # Merges the contents of +fm+ into the current instance
489
+ #
490
+ # NOTE: where any element is found in both merging instances the count
491
+ # will be a combination of the two counts
397
492
  def merge! fm
398
493
 
399
494
  fm.each do |k, v|
400
495
 
401
- if not @counts.has_key? k
496
+ if not @elements.has_key? k
402
497
 
403
- @counts[k] = v
498
+ @elements[k] = v
404
499
  else
405
500
 
406
- @counts[k] += v
501
+ @elements[k] += v
407
502
  end
408
503
  @count += v
409
504
  end
@@ -411,16 +506,34 @@ class FrequencyMap
411
506
  self
412
507
  end
413
508
 
414
- def push element, count = 1
509
+ # Pushes the +element+ and +count+. If the +element+ already exists,
510
+ # +count+ will be added to the existing count; otherwise it will be
511
+ # +count+
512
+ #
513
+ # === Signature
514
+ #
515
+ # * *Parameters:*
516
+ # - +key+ The element key
517
+ # - +count+ (Integer) The count by which to adjust
518
+ #
519
+ # === Exceptions
520
+ # - +::RangeError+ raised if the value of +count+ results in a negative count for the given element
521
+ # - +::TypeError+ if +count+ is not an +::Integer+
522
+ def push key, count = 1
523
+
524
+ raise TypeError, "'count' parameter must be of type #{::Integer}, but was of type #{count.class}" unless Integer === count
525
+
526
+ initial_count = @elements[key] || 0
527
+ resulting_count = initial_count + count
415
528
 
416
- raise TypeError, "count ('#{count}') must in an instance of #{::Integer}, but #{count.class} provided" unless count.kind_of? ::Integer
529
+ raise RangeError, "count for element '#{key}' cannot be made negative" if resulting_count < 0
417
530
 
418
- if not @counts.has_key? element
531
+ if 0 == resulting_count
419
532
 
420
- @counts[element] = count
533
+ @elements.delete key
421
534
  else
422
535
 
423
- @counts[element] += count
536
+ @elements[key] = resulting_count
424
537
  end
425
538
  @count += count
426
539
 
@@ -429,7 +542,7 @@ class FrequencyMap
429
542
 
430
543
  def shift
431
544
 
432
- r = @counts.shift
545
+ r = @elements.shift
433
546
 
434
547
  @count -= r[1] if ::Array === r
435
548
 
@@ -438,42 +551,52 @@ class FrequencyMap
438
551
 
439
552
  alias size length
440
553
 
441
- def store key, value
554
+ # Causes an element with the given +key+ and +count+ to be stored. If an
555
+ # element with the given +key+ already exists, its count will be adjusted,
556
+ # as will the total count
557
+ #
558
+ # === Return
559
+ # +true+ if the element was inserted; +false+ if the element was
560
+ # overwritten
561
+ def store key, count
442
562
 
443
- case value
444
- when ::NilClass, ::Integer
445
- ;
446
- else
447
- raise TypeError, "value ('#{value}') must be of type #{::Integer}, but was of type #{value.class}"
448
- end
563
+ raise TypeError, "'count' parameter must be of type #{::Integer}, but was of type #{count.class}" unless Integer === count
564
+
565
+ old_count = @elements[key] || 0
449
566
 
450
- key_count = @counts[key] || 0
567
+ @elements.store key, count
451
568
 
452
- @counts.store key, value
569
+ @count += count - old_count
453
570
 
454
- @count += value - key_count
571
+ old_count == 0
455
572
  end
456
573
 
574
+ # Converts instance to an array of +[key,value]+ pairs
457
575
  def to_a
458
576
 
459
- @counts.to_a
577
+ @elements.to_a
460
578
  end
461
579
 
580
+ # Obtains reference to internal hash instance (which must *not* be modified)
462
581
  def to_h
463
582
 
464
- @counts.to_h
583
+ @elements.to_h
465
584
  end
466
585
 
586
+ # Obtains equivalent hash to instance
467
587
  def to_hash
468
588
 
469
- @counts.to_hash
589
+ @elements.to_hash
470
590
  end
471
591
 
472
- alias to_s inspect
592
+ def to_s
593
+
594
+ @elements.to_s
595
+ end
473
596
 
474
597
  def values
475
598
 
476
- @counts.values
599
+ @elements.values
477
600
  end
478
601
  end # class FrequencyMap
479
602
 
@@ -5,13 +5,13 @@
5
5
  # Purpose: multimap container
6
6
  #
7
7
  # Created: 21st March 2007
8
- # Updated: 30th July 2017
8
+ # Updated: 12th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2007-2017, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2007-2019, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -202,6 +202,8 @@ class MultiMap < ::Hash
202
202
 
203
203
  def each_key
204
204
 
205
+ return @inner.each_key unless block_given?
206
+
205
207
  @inner.each_key { |key| yield key }
206
208
  end
207
209
 
@@ -6,13 +6,13 @@
6
6
  # module
7
7
  #
8
8
  # Created: 3rd June 2017
9
- # Updated: 28th July 2017
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2017, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2017-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -77,10 +77,10 @@ module BoolParser
77
77
  #
78
78
  # === Signature
79
79
  #
80
- # * *Parameters*:
80
+ # * *Parameters:*
81
81
  # - +options+:: An options hash, containing any of the following options
82
82
  #
83
- # * *Options*:
83
+ # * *Options:*
84
84
  # - +:false_values+:: [::Array] An array of strings or regular
85
85
  # expressions against which to match for false value. Defaults to
86
86
  # +DEFAULT_FALSE_VALUES+
@@ -6,13 +6,13 @@
6
6
  # module
7
7
  #
8
8
  # Created: 21st November 2017
9
- # Updated: 1st August 2018
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2017-2018, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2017-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -155,7 +155,7 @@ module IntegerParser
155
155
  #
156
156
  # === Signature
157
157
  #
158
- # * *Parameters*:
158
+ # * *Parameters:*
159
159
  # - +arg+:: The argument to be converted (to +Fixnum+ or +Bignum+)
160
160
  # - +base+:: A value of 0, or between 2 and 36. Defaults to 0
161
161
  # - +options+:: An options hash, containing any of the following
@@ -166,7 +166,7 @@ module IntegerParser
166
166
  # take additional action. If the block returns then its return value
167
167
  # will be returned to the caller
168
168
  #
169
- # * *Options*:
169
+ # * *Options:*
170
170
  # - +:default+:: A default value to be used when +arg+ is +nil+ or
171
171
  # cannot be converted by (the original) +Kernel#Integer+
172
172
  # - +:nil+:: Returns +nil+ if +arg+ is +nil+ or cannot be
@@ -5,13 +5,13 @@
5
5
  # Purpose: Adds a unique() method to the Enumerable module
6
6
  #
7
7
  # Created: 5th March 2007
8
- # Updated: 4th April 2016
8
+ # Updated: 12th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2007-2016, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2007-2019, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -55,6 +55,9 @@ module Enumerable
55
55
  # Removes all duplicate elements in a sequence subject to an optional
56
56
  # two-parameter block in order to return an array containing unique
57
57
  # elements
58
+ #
59
+ # [ 1, 2, 3 ].unique # => [ 1, 2, 3 ]
60
+ # [ 1, 2, 1, 3 ].unique # => [ 1, 2, 3 ]
58
61
  def unique(&block)
59
62
 
60
63
  if not block
@@ -62,38 +65,25 @@ module Enumerable
62
65
  return unique { |a, b| a == b }
63
66
  else
64
67
 
65
- if block.arity != 2
66
-
67
- raise ArgumentError, "block requires two parameters"
68
- end
68
+ raise ArgumentError, "block requires two parameters" unless block.arity == 2
69
69
 
70
70
  ar = self.to_a
71
71
 
72
72
  return ar if ar.length < 2
73
73
 
74
- ar = ar.clone
75
-
76
- i = 0
77
-
78
- while i < ar.length do
74
+ r = []
75
+ h = {}
79
76
 
80
- j = i + 1
77
+ ar.each do |v|
81
78
 
82
- while j < ar.length do
79
+ unless h.has_key?(v)
83
80
 
84
- if yield ar[i], ar[j]
85
-
86
- ar.delete_at(j)
87
- else
88
-
89
- j = j + 1
90
- end
81
+ r << v
82
+ h[v] = nil
91
83
  end
92
-
93
- i = i + 1
94
84
  end
95
85
 
96
- return ar
86
+ return r
97
87
  end
98
88
  end
99
89
  end # module Enumerable
@@ -5,13 +5,13 @@
5
5
  # Purpose: Adds a Integer 'overload' to the Kernel module
6
6
  #
7
7
  # Created: 21st November 2017
8
- # Updated: 18th May 2018
8
+ # Updated: 12th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2017-2018, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2017-2019, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -60,7 +60,7 @@ module Kernel
60
60
  #
61
61
  # === Signature
62
62
  #
63
- # * *Parameters*:
63
+ # * *Parameters:*
64
64
  # - +arg+:: The argument to be converted (to +Fixnum+ or +Bignum+)
65
65
  # - +base+:: A value of 0, or between 2 and 36. Defaults to 0
66
66
  # - +options+:: An options hash, containing any of the following
@@ -70,7 +70,7 @@ module Kernel
70
70
  # additional action. If the block returns then its return value will
71
71
  # be returned to the caller
72
72
  #
73
- # * *Options*:
73
+ # * *Options:*
74
74
  # - +:default+:: A default value to be used when +arg+ is +nil+ or
75
75
  # cannot be converted by (the original) +Kernel#Integer+
76
76
  # - +:nil+:: Returns +nil+ if +arg+ is +nil+ or cannot be
@@ -6,9 +6,9 @@ module Assertions
6
6
 
7
7
  unless respond_to? :assert_not
8
8
 
9
- def assert_not(test, failure_message = '')
9
+ def assert_not(expression, failure_message = '')
10
10
 
11
- assert !(test), failure_message
11
+ assert !(expression), failure_message
12
12
  end
13
13
  end
14
14
 
@@ -11,7 +11,7 @@ module Assertions
11
11
  #
12
12
  # === Signature
13
13
  #
14
- # * *Parameters*
14
+ # * *Parameters:*
15
15
  # - +type+:: [::Class] The type
16
16
  # - +message_spec+:: [::Symbol, ::Array, ::Hash] A specification
17
17
  # of message(s) received by the instances of +type+. If a
@@ -5,13 +5,13 @@
5
5
  # Purpose: Adds a writelines() method to the IO module
6
6
  #
7
7
  # Created: 13th April 2007
8
- # Updated: 2nd August 2017
8
+ # Updated: 12th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2007-2017, Matthew Wilson and Synesis Software
14
+ # Copyright (c) 2007-2019, Matthew Wilson and Synesis Software
15
15
  # All rights reserved.
16
16
  #
17
17
  # Redistribution and use in source and binary forms, with or without
@@ -125,12 +125,12 @@ $stderr.puts "#{self.class}.write_to_target_(target(#{target.class})='#{target}'
125
125
  #
126
126
  # === Signature
127
127
  #
128
- # * *Parameters*:
128
+ # * *Parameters:*
129
129
  # - +target+:: The target of the write, which may be a string containing the path or a stream instance that supports write
130
130
  # - +contents+:: The contents to be write, which may be a +Hash+, or an +Array+, or a +String+ containing delimited fields
131
131
  # - +options+:: An options hash, containing any of the following options
132
132
  #
133
- # * *Options*:
133
+ # * *Options:*
134
134
  # - +:column_separator+:: {optional} The column separator, to be applied between each field in the case where +contents+ is a +Hash+.
135
135
  # - +:eol_lookahead_limit+:: {optional} The number of content elements (line/pair) to inspect to determine whether element has a terminating end-of-line sequence. Defaults to 20. If 0, and +:line_separator+ is not specified, then will default to <tt>"\n"</tt>. If +nil+, then every line will be inspected.
136
136
  # - +:line_separator+:: {optional} The line separator, to be applied to the end of line created from each entry. When not specified, it will be deduced by inspecting +contents+ (according to +eol_lookahead_limit+).
@@ -6,13 +6,13 @@
6
6
  # module
7
7
  #
8
8
  # Created: 13th April 2016
9
- # Updated: 10th April 2019
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2016, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2016-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -114,7 +114,7 @@ module EndsWith
114
114
  #
115
115
  # === Signature
116
116
  #
117
- # * *Parameters*
117
+ # * *Parameters:*
118
118
  #
119
119
  # * *Required parameters*:
120
120
  # - +s+:: [String] The string to be evaluated
@@ -6,13 +6,13 @@
6
6
  # module
7
7
  #
8
8
  # Created: 25th January 2018
9
- # Updated: 10th April 2019
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2018, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2018-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -73,7 +73,7 @@ module NilIfEmpty
73
73
  #
74
74
  # === Signature
75
75
  #
76
- # * *Parameters*
76
+ # * *Parameters:*
77
77
  #
78
78
  # * *Required parameters*:
79
79
  # - +s+:: [String] The string to be evaluated
@@ -6,13 +6,13 @@
6
6
  # module
7
7
  #
8
8
  # Created: 25th January 2018
9
- # Updated: 10th April 2019
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2018, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2018-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -73,7 +73,7 @@ module NilIfWhitespace
73
73
  #
74
74
  # === Signature
75
75
  #
76
- # * *Parameters*
76
+ # * *Parameters:*
77
77
  #
78
78
  # * *Required parameters*:
79
79
  # - +s+:: [String] The string to be evaluated
@@ -6,13 +6,13 @@
6
6
  # module
7
7
  #
8
8
  # Created: 3rd June 2017
9
- # Updated: 10th April 2019
9
+ # Updated: 12th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2017, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2017-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -92,7 +92,7 @@ module QuoteIf
92
92
  #
93
93
  # === Signature
94
94
  #
95
- # * *Parameters*
95
+ # * *Parameters:*
96
96
  #
97
97
  # * *Required parameters*:
98
98
  # - +s+:: [String] The string to be evaluated
@@ -101,7 +101,7 @@ module QuoteIf
101
101
  # - +options+:: [Hash] Options that control the behaviour of the
102
102
  # method
103
103
  #
104
- # * *Options*:
104
+ # * *Options:*
105
105
  #
106
106
  # - +:quotes+:: [String, Array] A string that is used as the opening
107
107
  # and closing quotes, or an array whose first two elements are
@@ -5,7 +5,7 @@
5
5
  # Purpose: Version for Xqsr3 library
6
6
  #
7
7
  # Created: 3rd April 2016
8
- # Updated: 10th April 2019
8
+ # Updated: 12th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -50,7 +50,7 @@
50
50
  module Xqsr3
51
51
 
52
52
  # Current version of the Xqsr3 library
53
- VERSION = '0.31.2'
53
+ VERSION = '0.32.2'
54
54
 
55
55
  private
56
56
  VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
@@ -18,6 +18,10 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
18
18
  assert_equal 0, fm1.count
19
19
  assert fm1.empty?
20
20
  assert_equal 0, fm1.size
21
+
22
+ assert_nil fm1.default
23
+
24
+ assert_match /^#<Xqsr3::Containers::FrequencyMap:0x\d+:\s*@count\(\w+\)=0; @elements\(Hash\)={}\s*>$/, fm1.inspect
21
25
  end
22
26
 
23
27
  def test_class_operator_subscript_2
@@ -27,6 +31,8 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
27
31
  assert_equal 3, fm2.count
28
32
  assert_not fm2.empty?
29
33
  assert_equal 2, fm2.size
34
+
35
+ assert_match /^#<Xqsr3::Containers::FrequencyMap:0x\d+:\s*@count\(\w+\)=3; @elements\(Hash\)={.*abc.*def.*}\s*>$/, fm2.inspect
30
36
  end
31
37
 
32
38
  def test_class_operator_subscript_3
@@ -102,6 +108,52 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
102
108
  assert_equal 2, fm['jkl']
103
109
  end
104
110
 
111
+ def test_class_operator_subscript_9
112
+
113
+ fm = FrequencyMap::ByElement[ 'abc', 'def', 'abc', :x, 'x', :y ]
114
+
115
+ assert_false fm.empty? # => false
116
+ assert_equal 5, fm.size # => 5
117
+ assert_equal 6, fm.count # => 6
118
+ assert_equal 2, fm['abc'] # => 2
119
+ assert_equal 1, fm['def'] # => 1
120
+ assert_equal 0, fm['ghi'] # => 0
121
+ assert_equal 1, fm['x'] # => 1
122
+ assert_equal 0, fm['y'] # => 0
123
+ assert_equal 0, fm['z'] # => 0
124
+ assert_equal 1, fm[:x] # => 1
125
+ assert_equal 1, fm[:y] # => 1
126
+ assert_equal 0, fm[:z] # => 0
127
+
128
+ fm.push 'abc'
129
+
130
+ assert_false fm.empty?
131
+ assert_equal 5, fm.size
132
+ assert_equal 7, fm.count
133
+ assert_equal 3, fm['abc']
134
+
135
+ fm.push 'abc', 2
136
+
137
+ assert_false fm.empty?
138
+ assert_equal 5, fm.size
139
+ assert_equal 9, fm.count
140
+ assert_equal 5, fm['abc']
141
+
142
+ fm.push 'abc', -4
143
+
144
+ assert_false fm.empty?
145
+ assert_equal 5, fm.size
146
+ assert_equal 5, fm.count
147
+ assert_equal 1, fm['abc']
148
+
149
+ fm.push 'abc', -1
150
+
151
+ assert_false fm.empty?
152
+ assert_equal 4, fm.size
153
+ assert_equal 4, fm.count
154
+ assert_equal 0, fm['abc']
155
+ end
156
+
105
157
  def test_instance_operator_equals
106
158
 
107
159
  fm1 = FrequencyMap.new
@@ -150,13 +202,13 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
150
202
 
151
203
  fm = FrequencyMap.new
152
204
 
153
- assert_nil fm[:abc]
154
- assert_nil fm[:def]
205
+ assert_equal 0, fm[:abc]
206
+ assert_equal 0, fm[:def]
155
207
 
156
208
  fm << :abc
157
209
 
158
210
  assert_equal 1, fm[:abc]
159
- assert_nil fm[:def]
211
+ assert_equal 0, fm[:def]
160
212
 
161
213
  fm << :def << :def << :def
162
214
 
@@ -200,7 +252,7 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
200
252
 
201
253
  fm = FrequencyMap.new
202
254
 
203
- assert_nil fm['abc']
255
+ assert_equal 0, fm['abc']
204
256
  assert_equal 0, fm.count
205
257
  assert fm.empty?
206
258
  assert_equal 0, fm.size
@@ -214,7 +266,7 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
214
266
 
215
267
  fm.clear
216
268
 
217
- assert_nil fm['abc']
269
+ assert_equal 0, fm['abc']
218
270
  assert_equal 0, fm.count
219
271
  assert fm.empty?
220
272
  assert_equal 0, fm.size
@@ -757,3 +809,4 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
757
809
  end
758
810
  end
759
811
 
812
+
@@ -43,5 +43,29 @@ class Test_Enumerable_unique_test < Test::Unit::TestCase
43
43
 
44
44
  assert_equal [ 1, 2, 3, 4], dest
45
45
  end
46
+
47
+ def test_unique_very_large_sorted
48
+
49
+ max = 100000
50
+
51
+ src = (0...max).to_a * 2
52
+ exp = (0...max).to_a
53
+
54
+ dest = src.unique
55
+
56
+ assert_equal exp, dest
57
+ end
58
+
59
+ def test_unique_very_large_unsorted
60
+
61
+ max = 100000
62
+
63
+ src = ((0...max).to_a * 2).sort
64
+ exp = (0...max).to_a
65
+
66
+ dest = src.unique
67
+
68
+ assert_equal exp, dest
69
+ end
46
70
  end
47
71
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xqsr3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.2
4
+ version: 0.32.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Wilson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-11 00:00:00.000000000 Z
11
+ date: 2019-04-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  eXtensions by fine Quantum for Standard Ruby and 3rd-party libraries is a
@@ -21,6 +21,8 @@ extra_rdoc_files: []
21
21
  files:
22
22
  - LICENSE
23
23
  - README.md
24
+ - examples/count_word_frequencies.md
25
+ - examples/count_word_frequencies.rb
24
26
  - lib/xqsr3/array_utilities/join_with_or.rb
25
27
  - lib/xqsr3/command_line_utilities/map_option_string.rb
26
28
  - lib/xqsr3/containers/frequency_map.rb