sorted_containers 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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