sorted_containers 0.1.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,166 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "sorted_array"
4
+ require "forwardable"
4
5
 
5
6
  # The SortedContainers module provides data structures for sorted collections.
6
7
  module SortedContainers
7
- # The SortedHash class represents a sorted hash.
8
+ # rubocop:disable Metrics/ClassLength
9
+
10
+ # SortedHash is a hash that maintains the order of its keys.
11
+ #
12
+ # SortedHash has most of the same methods as a regular Hash,
13
+ # but also has the additional following methods:
14
+ #
15
+ # - #bisect_left
16
+ # - #bisect_right
17
+ # - #delete_at
18
+ # - #index
19
+ # - #last
20
+ # - #pop
21
+ #
22
+ # Addtionally, there are methods that work differently than their Hash counterparts.
23
+ # Generally, methods that use Entry Order will use the sort order of the keys instead.
24
+ # For example:
25
+ #
26
+ # h = Hash.new
27
+ # h[:b] = 1
28
+ # h[:a] = 2
29
+ # h.first # => [:b, 1]
30
+ #
31
+ # sh = SortedHash.new
32
+ # sh[:b] = 1
33
+ # sh[:a] = 2
34
+ # sh.first # => [:a, 2]
35
+ #
8
36
  class SortedHash
9
37
  include Enumerable
38
+ extend Forwardable
10
39
 
11
40
  # Initializes a new instance of the SortedHash class.
12
41
  #
42
+ # @param default_value [Object] The default value for the SortedHash.
13
43
  # @param load_factor [Integer] The load factor for the SortedHash.
14
- def initialize(hash = {}, load_factor: SortedArray::DEFAULT_LOAD_FACTOR)
15
- @hash = hash
16
- @sorted_array = SortedArray.new(hash.keys, load_factor: load_factor)
44
+ # @yield [Proc] The block to call to calculate the default value.
45
+ def initialize(default_value = nil, load_factor: SortedArray::DEFAULT_LOAD_FACTOR)
46
+ raise ArgumentError, "cannot specify both default value and block" if !default_value.nil? && block_given?
47
+
48
+ @internal_hash = if block_given?
49
+ Hash.new { |hash, key| hash[key] = yield(key) }
50
+ else
51
+ Hash.new(default_value)
52
+ end
53
+ @sorted_array = SortedArray.new(@internal_hash.keys, load_factor: load_factor)
54
+ end
55
+
56
+ # Creates a new instance of the SortedHash class.
57
+ #
58
+ # @param args [Array] The initial key-value pairs for the SortedHash.
59
+ # @return [SortedHash] A new instance of the SortedHash class.
60
+ def self.[](*args)
61
+ hash = new(nil)
62
+ hash.merge!(Hash[*args])
17
63
  end
18
64
 
65
+ # @!method first
66
+ # @see SortedArray#first
67
+ # @!method last
68
+ # @see SortedArray#last
69
+ # @!method bisect_left
70
+ # @see SortedArray#bisect_left
71
+ # @!method bisect_right
72
+ # @see SortedArray#bisect_right
73
+ def_delegators :@sorted_array,
74
+ :first,
75
+ :last,
76
+ :bisect_left,
77
+ :bisect_right
78
+
79
+ # @!method keys
80
+ # @see Hash#keys
81
+ def_delegator :@sorted_array, :to_a, :keys
82
+
83
+ # @!method any?
84
+ # @see Hash#any?
85
+ # @!method assoc
86
+ # @see Hash#assoc
87
+ # @!method deconstruct_keys
88
+ # @see Hash#deconstruct_keys
89
+ # @!method default
90
+ # @see Hash#default
91
+ # @!method default=
92
+ # @see Hash#default=
93
+ # @!method default_proc
94
+ # @see Hash#default_proc
95
+ # @!method dig
96
+ # @see Hash#dig
97
+ # @!method empty?
98
+ # @see Hash#empty?
99
+ # @!method eql?
100
+ # @see Hash#eql?
101
+ # @!method fetch
102
+ # @see Hash#fetch
103
+ # @!method fetch_values
104
+ # @see Hash#fetch_values
105
+ # @!method flatten
106
+ # @see Hash#flatten
107
+ # @!method has_key?
108
+ # @see Hash#has_key?
109
+ # @!method has_value?
110
+ # @see Hash#has_value?
111
+ # @!method hash
112
+ # @see Hash#hash
113
+ # @!method include?
114
+ # @see Hash#include?
115
+ # @!method key?
116
+ # @see Hash#key?
117
+ # @!method length
118
+ # @see Hash#length
119
+ # @!method member?
120
+ # @see Hash#member?
121
+ # @!method rassoc
122
+ # @see Hash#rassoc
123
+ # @!method size
124
+ # @see Hash#size
125
+ # @!method value?
126
+ # @see Hash#value?
127
+ # @!method values
128
+ # @see Hash#values
129
+ # @!method values_at
130
+ # @see Hash#values_at
131
+ def_delegators :@internal_hash,
132
+ :any?,
133
+ :assoc,
134
+ :deconstruct_keys,
135
+ :default,
136
+ :default=,
137
+ :default_proc,
138
+ :default_proc=,
139
+ :dig,
140
+ :empty?,
141
+ :eql?,
142
+ :fetch,
143
+ :fetch_values,
144
+ :flatten,
145
+ :has_key?,
146
+ :has_value?,
147
+ :hash,
148
+ :include?,
149
+ :key?,
150
+ :length,
151
+ :member?,
152
+ :rassoc,
153
+ :size,
154
+ :value?,
155
+ :values,
156
+ :values_at
157
+
19
158
  # Retrieves the value associated with the specified key.
20
159
  #
21
160
  # @param key [Object] The key to retrieve the value for.
22
161
  # @return [Object] The value associated with the key, or nil if the key is not found.
23
162
  def [](key)
24
- @hash[key]
163
+ @internal_hash[key]
25
164
  end
26
165
 
27
166
  # Associates the specified value with the specified key.
@@ -31,16 +170,47 @@ module SortedContainers
31
170
  # @param value [Object] The value to be associated with the key.
32
171
  # @return [Object] The value that was associated with the key.
33
172
  def []=(key, value)
34
- @sorted_array.delete(key) if @hash.key?(key)
173
+ @sorted_array.delete(key) if @internal_hash.key?(key)
35
174
  @sorted_array.add(key)
36
- @hash[key] = value
175
+ @internal_hash[key] = value
37
176
  end
38
177
 
39
- # Returns a string representation of the SortedHash.
40
- #
41
- # @return [String] A string representation of the SortedHash.
42
- def to_s
43
- "SortedHash({#{keys.map { |key| "#{key}: #{self[key]}" }.join(", ")}})"
178
+ # @see Hash#<
179
+ def <(other) = @internal_hash < other.internal_hash
180
+
181
+ # @see Hash#<=
182
+ def <=(other) = @internal_hash <= other.internal_hash
183
+
184
+ # @see Hash#==
185
+ def ==(other) = @internal_hash == other.internal_hash
186
+
187
+ # @see Hash#>
188
+ def >(other) = @internal_hash > other.internal_hash
189
+
190
+ # @see Hash#>=
191
+ def >=(other) = @internal_hash >= other.internal_hash
192
+
193
+ # Clears the SortedHash. After this operation, the SortedHash will be empty.
194
+ def clear
195
+ @internal_hash.clear
196
+ @sorted_array.clear
197
+ self
198
+ end
199
+
200
+ # @see Hash#compact
201
+ def compact
202
+ new_hash = dup
203
+ new_hash.compact!
204
+ new_hash
205
+ end
206
+
207
+ # @see Hash#compact!
208
+ def compact!
209
+ return nil if @internal_hash.compact!.nil?
210
+
211
+ @sorted_array.clear
212
+ @sorted_array.update(@internal_hash.keys)
213
+ self
44
214
  end
45
215
 
46
216
  # Deletes the key-value pair associated with the specified key.
@@ -48,12 +218,27 @@ module SortedContainers
48
218
  # @param key [Object] The key to delete.
49
219
  # @return [void]
50
220
  def delete(key)
51
- return unless @hash.key?(key)
221
+ return unless @internal_hash.key?(key)
52
222
 
53
- @hash.delete(key)
223
+ @internal_hash.delete(key)
54
224
  @sorted_array.delete(key)
55
225
  end
56
226
 
227
+ # @see Hash#delete_if
228
+ def delete_if(&block)
229
+ return enum_for(:delete_if) unless block_given?
230
+
231
+ @sorted_array.delete_if do |key|
232
+ if block.call(key, @internal_hash[key])
233
+ @internal_hash.delete(key)
234
+ true
235
+ else
236
+ false
237
+ end
238
+ end
239
+ self
240
+ end
241
+
57
242
  # Deletes the key-value pair at the specified index and returns it as a two-element array.
58
243
  #
59
244
  # @param index [Integer] The index of the key-value pair to delete.
@@ -62,10 +247,262 @@ module SortedContainers
62
247
  return nil if index.abs >= @sorted_array.size
63
248
 
64
249
  key = @sorted_array.delete_at(index)
65
- value = @hash.delete(key)
250
+ value = @internal_hash.delete(key)
66
251
  [key, value]
67
252
  end
68
253
 
254
+ # Iterates over each key-value pair in the SortedHash.
255
+ #
256
+ # @yield [key, value] The block to be executed for each key-value pair.
257
+ # @yieldparam key [Object] The key of the current key-value pair.
258
+ # @yieldparam value [Object] The value of the current key-value pair.
259
+ # @return [void]
260
+ def each(&block)
261
+ return enum_for(:each) unless block_given?
262
+
263
+ @sorted_array.each do |key|
264
+ value = @internal_hash[key]
265
+ block.call(key, value)
266
+ end
267
+ self
268
+ end
269
+ alias each_pair each
270
+
271
+ # @see Hash#each_key
272
+ def each_key(&block)
273
+ return enum_for(:each_key) unless block_given?
274
+
275
+ @sorted_array.each(&block)
276
+ self
277
+ end
278
+
279
+ # @see Hash#each_value
280
+ def each_value(&block)
281
+ return enum_for(:each_value) unless block_given?
282
+
283
+ @sorted_array.each { |key| block.call(@internal_hash[key]) }
284
+ self
285
+ end
286
+
287
+ # @see Hash#except
288
+ def except(*keys)
289
+ new_hash = dup
290
+ keys.each { |key| new_hash.delete(key) }
291
+ new_hash
292
+ end
293
+
294
+ # @see Hash#filter
295
+ def filter(&block)
296
+ return enum_for(:filter) unless block_given?
297
+
298
+ new_hash = dup
299
+ new_hash.filter!(&block)
300
+ new_hash
301
+ end
302
+ alias select filter
303
+
304
+ # rubocop:disable Metrics/MethodLength
305
+
306
+ # @see Hash#filter!
307
+ def filter!
308
+ return enum_for(:filter!) unless block_given?
309
+
310
+ changed = false
311
+ @sorted_array.filter! do |key|
312
+ if yield(key, @internal_hash[key])
313
+ true
314
+ else
315
+ @internal_hash.delete(key)
316
+ changed = true
317
+ false
318
+ end
319
+ end
320
+ changed ? self : nil
321
+ end
322
+ alias select! filter!
323
+
324
+ # rubocop:enable Metrics/MethodLength
325
+
326
+ # Values must be comparable for this method to work.
327
+ # @see Hash#invert
328
+ def invert
329
+ @internal_hash = @internal_hash.invert
330
+ @sorted_array.clear
331
+ @sorted_array.update(@internal_hash.keys)
332
+ self
333
+ end
334
+
335
+ # @see Hash#keep_if
336
+ def keep_if(&block)
337
+ return enum_for(:keep_if) unless block_given?
338
+
339
+ @sorted_array.keep_if do |key|
340
+ if block.call(key, @internal_hash[key])
341
+ true
342
+ else
343
+ @internal_hash.delete(key)
344
+ false
345
+ end
346
+ end
347
+ self
348
+ end
349
+
350
+ # @see Hash#key
351
+ def key(value)
352
+ @sorted_array.each do |key|
353
+ return key if @internal_hash[key] == value
354
+ end
355
+ nil
356
+ end
357
+
358
+ # @see Hash#merge
359
+ def merge(*other, &block)
360
+ new_hash = dup
361
+ new_hash.merge!(*other, &block)
362
+ new_hash
363
+ end
364
+
365
+ # @see Hash#merge!
366
+ def merge!(*other, &block)
367
+ other.each do |other_hash|
368
+ other_hash.each do |key, value|
369
+ value = block.call(key, @internal_hash[key], value) if block_given? && @internal_hash.key?(key)
370
+ @sorted_array.add(key) unless @internal_hash.key?(key)
371
+ @internal_hash[key] = value
372
+ end
373
+ end
374
+ self
375
+ end
376
+ alias update merge!
377
+
378
+ # @see Hash#rehash
379
+ # Also resorts the SortedHash.
380
+ def rehash
381
+ @internal_hash.rehash
382
+ @sorted_array.sort!
383
+ self
384
+ end
385
+
386
+ # @see Hash#reject
387
+ def reject(&block)
388
+ return enum_for(:reject) unless block_given?
389
+
390
+ filter { |key, value| !block.call(key, value) }
391
+ end
392
+
393
+ # @see Hash#reject!
394
+ def reject!(&block)
395
+ return enum_for(:reject!) unless block_given?
396
+
397
+ filter! { |key, value| !block.call(key, value) }
398
+ end
399
+
400
+ # @see Hash#replace
401
+ def replace(other)
402
+ @internal_hash.replace(other.internal_hash)
403
+ @sorted_array.clear
404
+ @sorted_array.update(@internal_hash.keys)
405
+ self
406
+ end
407
+
408
+ # @see Hash#slice
409
+ def slice(*keys)
410
+ new_hash = self.class.new(@internal_hash.default, load_factor: @sorted_array.load_factor)
411
+ keys.each do |key|
412
+ new_hash[key] = @internal_hash[key] if @internal_hash.key?(key)
413
+ end
414
+ new_hash
415
+ end
416
+
417
+ # @see Hash#store
418
+ def store(key, value)
419
+ @sorted_array.add(key) unless @internal_hash.key?(key)
420
+ @internal_hash[key] = value
421
+ end
422
+
423
+ # @see Hash#to_a
424
+ # Returns in sorted order by key.
425
+ def to_a
426
+ @sorted_array.map { |key| [key, @internal_hash[key]] }
427
+ end
428
+
429
+ # @see Hash#to_h
430
+ def to_h
431
+ if block_given?
432
+ @sorted_array.each_with_object({}) do |key, hash|
433
+ key_value = yield(key, @internal_hash[key])
434
+ hash[key_value.first] = key_value.last
435
+ end
436
+ else
437
+ @internal_hash.dup
438
+ end
439
+ end
440
+
441
+ # Converts SortedHash to a hash.
442
+ def to_hash
443
+ @internal_hash.dup
444
+ end
445
+
446
+ # @see Hash#to_proc
447
+ def to_proc
448
+ ->(key) { @internal_hash[key] }
449
+ end
450
+
451
+ # Returns a string representation of the SortedHash.
452
+ #
453
+ # @return [String] A string representation of the SortedHash.
454
+ def to_s
455
+ "SortedHash({#{keys.map { |key| "#{key}: #{self[key]}" }.join(", ")}})"
456
+ end
457
+ alias inspect to_s
458
+
459
+ # @see Hash#transform_keys
460
+ def transform_keys(&block)
461
+ return enum_for(:transform_keys) unless block_given?
462
+
463
+ new_hash = dup
464
+ new_hash.transform_keys!(&block)
465
+ new_hash
466
+ end
467
+
468
+ # @see Hash#transform_keys!
469
+ def transform_keys!(&block)
470
+ return enum_for(:transform_keys!) unless block_given?
471
+
472
+ @internal_hash.transform_keys!(&block)
473
+ @sorted_array.clear
474
+ @sorted_array.update(@internal_hash.keys)
475
+ self
476
+ end
477
+
478
+ # @see Hash#transform_values
479
+ def transform_values(&block)
480
+ return enum_for(:transform_values) unless block_given?
481
+
482
+ new_hash = dup
483
+ new_hash.transform_values!(&block)
484
+ new_hash
485
+ end
486
+
487
+ # @see Hash#transform_values!
488
+ def transform_values!(&block)
489
+ return enum_for(:transform_values!) unless block_given?
490
+
491
+ @internal_hash.transform_values!(&block)
492
+ self
493
+ end
494
+
495
+ # Returns the index of the given value.
496
+ # If the value is not found, returns nil.
497
+ #
498
+ # @param value [Object] The value to find the index of.
499
+ # @return [Integer, nil] The index of the value, or nil if the value is not found.
500
+ def index(value)
501
+ return nil unless @internal_hash.key?(value)
502
+
503
+ @sorted_array.bisect_left(value)
504
+ end
505
+
69
506
  # Retrieves the first key-value pair from the SortedHash as a two-element array.
70
507
  #
71
508
  # @return [Array] A two-element array containing the key and value of the first key-value pair.
@@ -73,7 +510,7 @@ module SortedContainers
73
510
  return nil if @sorted_array.empty?
74
511
 
75
512
  key = @sorted_array.first
76
- [key, @hash[key]]
513
+ [key, @internal_hash[key]]
77
514
  end
78
515
 
79
516
  # Removes the first key-value pair from the SortedHash and returns it as a two-element array.
@@ -83,7 +520,7 @@ module SortedContainers
83
520
  return nil if @sorted_array.empty?
84
521
 
85
522
  key = @sorted_array.last
86
- [key, @hash[key]]
523
+ [key, @internal_hash[key]]
87
524
  end
88
525
 
89
526
  # Removes the last key-value pair from the SortedHash and returns it as a two-element array.
@@ -93,7 +530,7 @@ module SortedContainers
93
530
  return nil if @sorted_array.empty?
94
531
 
95
532
  key = @sorted_array.pop
96
- value = @hash.delete(key)
533
+ value = @internal_hash.delete(key)
97
534
  [key, value]
98
535
  end
99
536
 
@@ -104,49 +541,20 @@ module SortedContainers
104
541
  return nil if @sorted_array.empty?
105
542
 
106
543
  key = @sorted_array.shift
107
- value = @hash.delete(key)
544
+ value = @internal_hash.delete(key)
108
545
  [key, value]
109
546
  end
110
547
 
111
- # Returns the number of key-value pairs in the SortedHash.
112
- #
113
- # @return [Integer] The number of key-value pairs.
114
- def bisect_left(key)
115
- @sorted_array.bisect_left(key)
116
- end
117
-
118
- # Returns the number of key-value pairs in the SortedHash.
119
- #
120
- # @return [Integer] The number of key-value pairs.
121
- def bisect_right(key)
122
- @sorted_array.bisect_right(key)
123
- end
124
-
125
- # Returns an array of all the keys in the SortedHash.
126
- #
127
- # @return [Array] An array of all the keys.
128
- def keys
129
- @sorted_array.to_a
130
- end
131
-
132
548
  # Returns an array of all the values in the SortedHash.
133
549
  #
134
550
  # @return [Array] An array of all the values.
135
551
  def values
136
- @sorted_array.to_a.map { |key| @hash[key] }
552
+ @sorted_array.to_a.map { |key| @internal_hash[key] }
137
553
  end
138
554
 
139
- # Iterates over each key-value pair in the SortedHash.
140
- #
141
- # @yield [key, value] The block to be executed for each key-value pair.
142
- # @yieldparam key [Object] The key of the current key-value pair.
143
- # @yieldparam value [Object] The value of the current key-value pair.
144
- # @return [void]
145
- def each(&block)
146
- @sorted_array.each do |key|
147
- value = @hash[key]
148
- block.call(key, value)
149
- end
150
- end
555
+ protected
556
+
557
+ attr_reader :sorted_array, :internal_hash
151
558
  end
559
+ # rubocop:enable Metrics/ClassLength
152
560
  end