sorted_containers 0.0.1 → 0.1.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/.rubocop.yml +0 -3
- data/CHANGELOG.md +5 -1
- data/README.md +133 -10
- data/lib/sorted_containers/sorted_array.rb +854 -0
- data/lib/sorted_containers/sorted_hash.rb +152 -0
- data/lib/sorted_containers/sorted_set.rb +82 -9
- data/lib/sorted_containers/version.rb +1 -1
- data/lib/sorted_containers.rb +2 -5
- metadata +9 -7
- data/lib/sorted_containers/sorted_list.rb +0 -174
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "sorted_array"
|
4
|
+
|
5
|
+
# The SortedContainers module provides data structures for sorted collections.
|
6
|
+
module SortedContainers
|
7
|
+
# The SortedHash class represents a sorted hash.
|
8
|
+
class SortedHash
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# Initializes a new instance of the SortedHash class.
|
12
|
+
#
|
13
|
+
# @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)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Retrieves the value associated with the specified key.
|
20
|
+
#
|
21
|
+
# @param key [Object] The key to retrieve the value for.
|
22
|
+
# @return [Object] The value associated with the key, or nil if the key is not found.
|
23
|
+
def [](key)
|
24
|
+
@hash[key]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Associates the specified value with the specified key.
|
28
|
+
# If the key already exists, the previous value will be replaced.
|
29
|
+
#
|
30
|
+
# @param key [Object] The key to associate the value with.
|
31
|
+
# @param value [Object] The value to be associated with the key.
|
32
|
+
# @return [Object] The value that was associated with the key.
|
33
|
+
def []=(key, value)
|
34
|
+
@sorted_array.delete(key) if @hash.key?(key)
|
35
|
+
@sorted_array.add(key)
|
36
|
+
@hash[key] = value
|
37
|
+
end
|
38
|
+
|
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(", ")}})"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Deletes the key-value pair associated with the specified key.
|
47
|
+
#
|
48
|
+
# @param key [Object] The key to delete.
|
49
|
+
# @return [void]
|
50
|
+
def delete(key)
|
51
|
+
return unless @hash.key?(key)
|
52
|
+
|
53
|
+
@hash.delete(key)
|
54
|
+
@sorted_array.delete(key)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Deletes the key-value pair at the specified index and returns it as a two-element array.
|
58
|
+
#
|
59
|
+
# @param index [Integer] The index of the key-value pair to delete.
|
60
|
+
# @return [Array] A two-element array containing the key and value of the deleted key-value pair.
|
61
|
+
def delete_at(index)
|
62
|
+
return nil if index.abs >= @sorted_array.size
|
63
|
+
|
64
|
+
key = @sorted_array.delete_at(index)
|
65
|
+
value = @hash.delete(key)
|
66
|
+
[key, value]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Retrieves the first key-value pair from the SortedHash as a two-element array.
|
70
|
+
#
|
71
|
+
# @return [Array] A two-element array containing the key and value of the first key-value pair.
|
72
|
+
def first
|
73
|
+
return nil if @sorted_array.empty?
|
74
|
+
|
75
|
+
key = @sorted_array.first
|
76
|
+
[key, @hash[key]]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Removes the first key-value pair from the SortedHash and returns it as a two-element array.
|
80
|
+
#
|
81
|
+
# @return [Array] A two-element array containing the key and value of the first key-value pair.
|
82
|
+
def last
|
83
|
+
return nil if @sorted_array.empty?
|
84
|
+
|
85
|
+
key = @sorted_array.last
|
86
|
+
[key, @hash[key]]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Removes the last key-value pair from the SortedHash and returns it as a two-element array.
|
90
|
+
#
|
91
|
+
# @return [Array] A two-element array containing the key and value of the last key-value pair.
|
92
|
+
def pop
|
93
|
+
return nil if @sorted_array.empty?
|
94
|
+
|
95
|
+
key = @sorted_array.pop
|
96
|
+
value = @hash.delete(key)
|
97
|
+
[key, value]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Removes the first key-value pair from the SortedHash and returns it as a two-element array.
|
101
|
+
#
|
102
|
+
# @return [Array] A two-element array containing the key and value of the first key-value pair.
|
103
|
+
def shift
|
104
|
+
return nil if @sorted_array.empty?
|
105
|
+
|
106
|
+
key = @sorted_array.shift
|
107
|
+
value = @hash.delete(key)
|
108
|
+
[key, value]
|
109
|
+
end
|
110
|
+
|
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
|
+
# Returns an array of all the values in the SortedHash.
|
133
|
+
#
|
134
|
+
# @return [Array] An array of all the values.
|
135
|
+
def values
|
136
|
+
@sorted_array.to_a.map { |key| @hash[key] }
|
137
|
+
end
|
138
|
+
|
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
|
151
|
+
end
|
152
|
+
end
|
@@ -1,16 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
-
require_relative "
|
4
|
+
require_relative "sorted_array"
|
5
5
|
|
6
6
|
# A module that provides sorted container data structures.
|
7
7
|
module SortedContainers
|
8
8
|
# The SortedSet class is a sorted set implementation.
|
9
9
|
class SortedSet
|
10
|
+
include Enumerable
|
11
|
+
|
10
12
|
# Initializes a new instance of the SortedSet class.
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
#
|
14
|
+
# @param iterable [Array] The initial elements of the sorted set.
|
15
|
+
# @param load_factor [Integer] The load factor for the sorted set.
|
16
|
+
def initialize(iterable = [], load_factor: SortedArray::DEFAULT_LOAD_FACTOR)
|
17
|
+
@set = Set.new(iterable)
|
18
|
+
@list = SortedContainers::SortedArray.new(@set.to_a, load_factor: load_factor)
|
14
19
|
end
|
15
20
|
|
16
21
|
# Adds an item to the sorted set.
|
@@ -22,12 +27,34 @@ module SortedContainers
|
|
22
27
|
@set.add(item)
|
23
28
|
@list.add(item)
|
24
29
|
end
|
30
|
+
alias << add
|
25
31
|
|
26
|
-
#
|
32
|
+
# Retrieves the item at the specified index.
|
27
33
|
#
|
28
|
-
# @param
|
29
|
-
def
|
30
|
-
|
34
|
+
# @param index [Integer] The index of the item to retrieve.
|
35
|
+
def [](index)
|
36
|
+
@list[index]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a string representation of the sorted set.
|
40
|
+
#
|
41
|
+
# @return [String] A string representation of the sorted set.
|
42
|
+
def to_s
|
43
|
+
"SortedSet(#{to_a.join(", ")})"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Retrieves the first item in the sorted set.
|
47
|
+
#
|
48
|
+
# @return [Object] The first item.
|
49
|
+
def first
|
50
|
+
@list.first
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieves the last item in the sorted set.
|
54
|
+
#
|
55
|
+
# @return [Object] The last item.
|
56
|
+
def last
|
57
|
+
@list.last
|
31
58
|
end
|
32
59
|
|
33
60
|
# Removes an item from the sorted set.
|
@@ -37,7 +64,18 @@ module SortedContainers
|
|
37
64
|
return unless @set.include?(item)
|
38
65
|
|
39
66
|
@set.delete(item)
|
40
|
-
@list.
|
67
|
+
@list.delete(item)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Removes the item at the specified index.
|
71
|
+
#
|
72
|
+
# @param index [Integer] The index of the item to remove.
|
73
|
+
def delete_at(index)
|
74
|
+
return if index.abs >= @list.size
|
75
|
+
|
76
|
+
item = @list.delete_at(index)
|
77
|
+
@set.delete(item)
|
78
|
+
item
|
41
79
|
end
|
42
80
|
|
43
81
|
# Returns the number of items in the sorted set.
|
@@ -55,6 +93,41 @@ module SortedContainers
|
|
55
93
|
@set.include?(item)
|
56
94
|
end
|
57
95
|
|
96
|
+
# Returns an index to insert `value` in the sorted list.
|
97
|
+
#
|
98
|
+
# If the `value` is already present, the insertion point will be before
|
99
|
+
# (to the left of) any existing values.
|
100
|
+
#
|
101
|
+
# Runtime complexity: `O(log(n))` -- approximate.
|
102
|
+
#
|
103
|
+
# @see SortedArray#bisect_left
|
104
|
+
# @param value [Object] The value to insert.
|
105
|
+
# @return [Integer] The index to insert the value.
|
106
|
+
def bisect_left(value)
|
107
|
+
@list.bisect_left(value)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns an index to insert `value` in the sorted list.
|
111
|
+
#
|
112
|
+
# If the `value` is already present, the insertion point will be after
|
113
|
+
# (to the right of) any existing values.
|
114
|
+
#
|
115
|
+
# Runtime complexity: `O(log(n))` -- approximate.
|
116
|
+
#
|
117
|
+
# @see SortedArray#bisect_right
|
118
|
+
# @param value [Object] The value to insert.
|
119
|
+
# @return [Integer] The index to insert the value.
|
120
|
+
def bisect_right(value)
|
121
|
+
@list.bisect_right(value)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the items in the sorted set as an array.
|
125
|
+
#
|
126
|
+
# @return [Array] The items in the sorted set.
|
127
|
+
def to_a
|
128
|
+
@list.to_a
|
129
|
+
end
|
130
|
+
|
58
131
|
# Iterates over each item in the sorted set.
|
59
132
|
#
|
60
133
|
# @yield [item] Gives each item to the block.
|
data/lib/sorted_containers.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "sorted_containers/version"
|
4
|
-
require_relative "sorted_containers/
|
4
|
+
require_relative "sorted_containers/sorted_array"
|
5
5
|
require_relative "sorted_containers/sorted_set"
|
6
|
-
|
7
|
-
module SortedContainers
|
8
|
-
class Error < StandardError; end
|
9
|
-
end
|
6
|
+
require_relative "sorted_containers/sorted_hash"
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorted_containers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garrison Jensen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description: A collection of sorted containers including
|
14
|
-
SortedSet.
|
13
|
+
description: A collection of sorted containers including SortedArray, SortedDict,
|
14
|
+
and SortedSet.
|
15
15
|
email:
|
16
16
|
- garrison.jensen@gmail.com
|
17
17
|
executables: []
|
@@ -26,7 +26,8 @@ files:
|
|
26
26
|
- README.md
|
27
27
|
- Rakefile
|
28
28
|
- lib/sorted_containers.rb
|
29
|
-
- lib/sorted_containers/
|
29
|
+
- lib/sorted_containers/sorted_array.rb
|
30
|
+
- lib/sorted_containers/sorted_hash.rb
|
30
31
|
- lib/sorted_containers/sorted_set.rb
|
31
32
|
- lib/sorted_containers/version.rb
|
32
33
|
- sig/sorted_containers.rbs
|
@@ -54,8 +55,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
55
|
- !ruby/object:Gem::Version
|
55
56
|
version: '0'
|
56
57
|
requirements: []
|
57
|
-
rubygems_version: 3.5.
|
58
|
+
rubygems_version: 3.5.9
|
58
59
|
signing_key:
|
59
60
|
specification_version: 4
|
60
|
-
summary: A collection of sorted containers including
|
61
|
+
summary: A collection of sorted containers including SortedArray, SortedDict, and
|
62
|
+
SortedSet.
|
61
63
|
test_files: []
|
@@ -1,174 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# The SortedContainers module provides data structures for sorted collections.
|
4
|
-
module SortedContainers
|
5
|
-
class Error < StandardError; end
|
6
|
-
|
7
|
-
# The SortedList class is a sorted list implementation.
|
8
|
-
class SortedList
|
9
|
-
DEFAULT_LOAD_FACTOR = 1000
|
10
|
-
|
11
|
-
attr_reader :size
|
12
|
-
|
13
|
-
# Initializes a new SortedList object.
|
14
|
-
#
|
15
|
-
# @param iterable [Enumerable] An optional iterable object to initialize the list with.
|
16
|
-
def initialize(iterable = [])
|
17
|
-
@lists = []
|
18
|
-
@maxes = []
|
19
|
-
@load_factor = DEFAULT_LOAD_FACTOR
|
20
|
-
@size = 0
|
21
|
-
update(iterable)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Adds a value to the sorted list.
|
25
|
-
#
|
26
|
-
# @param value [Object] The value to add.
|
27
|
-
def add(value)
|
28
|
-
i = bisect_right(@maxes, value)
|
29
|
-
if i == @maxes.size
|
30
|
-
@lists.push([value])
|
31
|
-
@maxes.push(value)
|
32
|
-
else
|
33
|
-
idx = bisect_right(@lists[i], value)
|
34
|
-
@lists[i].insert(idx, value)
|
35
|
-
@maxes[i] = @lists[i].last
|
36
|
-
expand(i) if @lists[i].size > (@load_factor * 2)
|
37
|
-
end
|
38
|
-
@size += 1
|
39
|
-
end
|
40
|
-
|
41
|
-
# Adds a value to the sorted list using the << operator.
|
42
|
-
#
|
43
|
-
# @param value [Object] The value to add.
|
44
|
-
def <<(value)
|
45
|
-
add(value)
|
46
|
-
end
|
47
|
-
|
48
|
-
def remove(value)
|
49
|
-
i = bisect_left(@maxes, value)
|
50
|
-
raise "Value not found: #{value}" if i == @maxes.size
|
51
|
-
|
52
|
-
idx = bisect_left(@lists[i], value)
|
53
|
-
raise "Value not found: #{value}" unless @lists[i][idx] == value
|
54
|
-
|
55
|
-
internal_delete(i, idx)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Retrieves the value at the specified index.
|
59
|
-
#
|
60
|
-
# @param index [Integer] The index of the value to retrieve.
|
61
|
-
# @return [Object] The value at the specified index.
|
62
|
-
def [](index)
|
63
|
-
raise "Index out of range" if index.negative? || index >= @size
|
64
|
-
|
65
|
-
@lists.each do |sublist|
|
66
|
-
return sublist[index] if index < sublist.size
|
67
|
-
|
68
|
-
index -= sublist.size
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Deletes the value at the specified index.
|
73
|
-
#
|
74
|
-
# @param index [Integer] The index of the value to delete.
|
75
|
-
def delete_at(index)
|
76
|
-
raise "Index out of range" if index.negative? || index >= @size
|
77
|
-
|
78
|
-
deleted = false
|
79
|
-
@lists.each_with_index do |sublist, sublist_index|
|
80
|
-
if index < sublist.size
|
81
|
-
internal_delete(sublist_index, index)
|
82
|
-
deleted = true
|
83
|
-
break
|
84
|
-
else
|
85
|
-
index -= sublist.size
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
raise "Index out of range" unless deleted
|
90
|
-
end
|
91
|
-
|
92
|
-
# Clears the sorted list, removing all values.
|
93
|
-
def clear
|
94
|
-
@lists.clear
|
95
|
-
@maxes.clear
|
96
|
-
@size = 0
|
97
|
-
end
|
98
|
-
|
99
|
-
# Checks if the sorted list contains a value.
|
100
|
-
#
|
101
|
-
# @param value [Object] The value to check.
|
102
|
-
# @return [Boolean] True if the value is found, false otherwise.
|
103
|
-
def contains(value)
|
104
|
-
i = bisect_left(@maxes, value)
|
105
|
-
return false if i == @maxes.size
|
106
|
-
|
107
|
-
sublist = @lists[i]
|
108
|
-
idx = bisect_left(sublist, value)
|
109
|
-
idx < sublist.size && sublist[idx] == value
|
110
|
-
end
|
111
|
-
|
112
|
-
# Converts the sorted list to an array.
|
113
|
-
#
|
114
|
-
# @return [Array] An array representation of the sorted list.
|
115
|
-
def to_a
|
116
|
-
@lists.flatten
|
117
|
-
end
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
|
-
# Performs a left bisect on the array.
|
122
|
-
#
|
123
|
-
# @param array [Array] The array to bisect.
|
124
|
-
# @param value [Object] The value to bisect with.
|
125
|
-
# @return [Integer] The index where the value should be inserted.
|
126
|
-
def bisect_left(array, value)
|
127
|
-
array.bsearch_index { |x| x >= value } || array.size
|
128
|
-
end
|
129
|
-
|
130
|
-
# Performs a right bisect on the array.
|
131
|
-
#
|
132
|
-
# @param array [Array] The array to bisect.
|
133
|
-
# @param value [Object] The value to bisect with.
|
134
|
-
# @return [Integer] The index where the value should be inserted.
|
135
|
-
def bisect_right(array, value)
|
136
|
-
array.bsearch_index { |x| x > value } || array.size
|
137
|
-
end
|
138
|
-
|
139
|
-
# Expands a sublist if it exceeds the load factor.
|
140
|
-
#
|
141
|
-
# @param sublist_index [Integer] The index of the sublist to expand.
|
142
|
-
def expand(sublist_index)
|
143
|
-
sublist = @lists[sublist_index]
|
144
|
-
return unless sublist.size > (@load_factor * 2)
|
145
|
-
|
146
|
-
half = sublist.slice!(@load_factor, sublist.size - @load_factor)
|
147
|
-
@lists.insert(sublist_index + 1, half)
|
148
|
-
@maxes[sublist_index] = @lists[sublist_index].last
|
149
|
-
@maxes.insert(sublist_index + 1, half.last)
|
150
|
-
end
|
151
|
-
|
152
|
-
# Deletes a value from a sublist.
|
153
|
-
#
|
154
|
-
# @param sublist_index [Integer] The index of the sublist.
|
155
|
-
# @param idx [Integer] The index of the value to delete.
|
156
|
-
def internal_delete(sublist_index, idx)
|
157
|
-
@lists[sublist_index].delete_at(idx)
|
158
|
-
if @lists[sublist_index].empty?
|
159
|
-
@lists.delete_at(sublist_index)
|
160
|
-
@maxes.delete_at(sublist_index)
|
161
|
-
else
|
162
|
-
@maxes[sublist_index] = @lists[sublist_index].last
|
163
|
-
end
|
164
|
-
@size -= 1
|
165
|
-
end
|
166
|
-
|
167
|
-
# Updates the sorted list with values from an iterable object.
|
168
|
-
#
|
169
|
-
# @param iterable [Enumerable] The iterable object to update the list with.
|
170
|
-
def update(iterable)
|
171
|
-
iterable.each { |item| add(item) }
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|