ninjudd-ordered_set 0.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.
- data/README.rdoc +35 -0
- data/VERSION.yml +4 -0
- data/lib/ordered_set.rb +157 -0
- data/test/ordered_set_test.rb +300 -0
- data/test/test_helper.rb +11 -0
- metadata +58 -0
data/README.rdoc
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
= OrderedSet
|
2
|
+
|
3
|
+
OrderSet is an class for storing sets of objects. Unlike a Set, it maintains the order of
|
4
|
+
the objects, but unlike an Array, each object can only exist once, and checking for
|
5
|
+
inclusion takes constant time.
|
6
|
+
|
7
|
+
== Usage:
|
8
|
+
|
9
|
+
s = [1,3,2,3,4,3].to_ordered_set
|
10
|
+
s.to_a
|
11
|
+
# => [1,3,2,4]
|
12
|
+
|
13
|
+
t = OrderedSet.new([6,7,5,6,5])
|
14
|
+
t.to_a
|
15
|
+
# => [6,7,5]
|
16
|
+
|
17
|
+
(s + t).to_a
|
18
|
+
# => [1,3,2,4,6,7,5]
|
19
|
+
|
20
|
+
s << 5
|
21
|
+
s.to_a
|
22
|
+
# => [1,3,2,4,5]
|
23
|
+
|
24
|
+
s.delete(1)
|
25
|
+
s.to_a
|
26
|
+
# => [3,2,4,5]
|
27
|
+
|
28
|
+
== Install:
|
29
|
+
|
30
|
+
sudo gem install ninjudd-deep_clonable -s http://gems.github.com
|
31
|
+
sudo gem install ninjudd-ordered_set -s http://gems.github.com
|
32
|
+
|
33
|
+
== License:
|
34
|
+
|
35
|
+
Copyright (c) 2008 Justin Balthrop, Geni.com; Published under The MIT License, see LICENSE
|
data/VERSION.yml
ADDED
data/lib/ordered_set.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'deep_clonable'
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
class OrderedSet
|
6
|
+
include Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
deep_clonable
|
10
|
+
|
11
|
+
def initialize(items = [])
|
12
|
+
@order = []
|
13
|
+
@position = {}
|
14
|
+
replace(items)
|
15
|
+
end
|
16
|
+
|
17
|
+
def_delegators :@order, :each, :join, :[], :first, :last, :slice, :size, :length, :empty?
|
18
|
+
alias limit slice
|
19
|
+
|
20
|
+
def replace(items)
|
21
|
+
clear
|
22
|
+
items.each do |item|
|
23
|
+
self << item
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear
|
29
|
+
@order.clear
|
30
|
+
@position.clear
|
31
|
+
end
|
32
|
+
|
33
|
+
def include?(item)
|
34
|
+
@position.has_key?(item)
|
35
|
+
end
|
36
|
+
|
37
|
+
def <<(item)
|
38
|
+
return if include?(item)
|
39
|
+
@position[item] = @order.size
|
40
|
+
@order << item
|
41
|
+
end
|
42
|
+
|
43
|
+
clone_method :+, :add!
|
44
|
+
def add!(items)
|
45
|
+
items.each do |item|
|
46
|
+
self << item
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
alias concat add!
|
51
|
+
|
52
|
+
def unshift!(items)
|
53
|
+
need_reindex = false
|
54
|
+
items.each do |item|
|
55
|
+
next if include?(item)
|
56
|
+
@order.unshift(item)
|
57
|
+
need_reindex = true # Need to recalculate the positions.
|
58
|
+
end
|
59
|
+
reindex if need_reindex
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def unshift(item)
|
64
|
+
unshift!([item])
|
65
|
+
end
|
66
|
+
|
67
|
+
clone_method :-, :subtract!
|
68
|
+
def subtract!(items)
|
69
|
+
@order.replace(self.to_a - items.to_a)
|
70
|
+
reindex
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
clone_method :&, :intersect!
|
75
|
+
def intersect!(items)
|
76
|
+
@order.replace(self.to_a & items.to_a)
|
77
|
+
reindex
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def reorder!(items)
|
82
|
+
new_order = (items.to_a & self.to_a) + self.to_a
|
83
|
+
@order.replace(new_order.uniq)
|
84
|
+
reindex
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def reverse_reorder!(items)
|
89
|
+
return self if items.empty?
|
90
|
+
reverse!.reorder!(items).reverse!
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_ary
|
94
|
+
@order.dup
|
95
|
+
end
|
96
|
+
|
97
|
+
def index(item)
|
98
|
+
@position[item]
|
99
|
+
end
|
100
|
+
|
101
|
+
def delete(item)
|
102
|
+
@order.delete(item) do
|
103
|
+
return block_given? ? yield : nil
|
104
|
+
end
|
105
|
+
reindex
|
106
|
+
item
|
107
|
+
end
|
108
|
+
|
109
|
+
def delete_at(index, &block)
|
110
|
+
delete(self[index], &block)
|
111
|
+
end
|
112
|
+
|
113
|
+
[:sort_by, :sort, :reverse, :collect, :map, :compact, :reject].each do |method_name|
|
114
|
+
eval %{
|
115
|
+
def #{method_name}!(*args, &block)
|
116
|
+
new_order = @order.send(:#{method_name}, *args, &block)
|
117
|
+
replace(new_order)
|
118
|
+
self
|
119
|
+
end
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
def select!
|
124
|
+
reject! {|item| not yield(item)}
|
125
|
+
end
|
126
|
+
|
127
|
+
# Array#slice! is somewhat inconsistent with the other bang methods,
|
128
|
+
# and this emulates that. For the more consistent behavior use limit!
|
129
|
+
def slice!(*args)
|
130
|
+
removed_slice = @order.slice!(*args)
|
131
|
+
reindex
|
132
|
+
removed_slice
|
133
|
+
end
|
134
|
+
|
135
|
+
clone_method :limit
|
136
|
+
def limit!(limit, offset = 0)
|
137
|
+
new_order = @order.slice(offset, limit) || []
|
138
|
+
replace(new_order)
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def reindex
|
145
|
+
@position.clear
|
146
|
+
@order.each_with_index do |item, index|
|
147
|
+
@position[item] = index
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
module Enumerable
|
154
|
+
def to_ordered_set
|
155
|
+
self.kind_of?(OrderedSet) ? self : OrderedSet.new(self)
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class OrderedSetTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should 'create an ordered_set' do
|
6
|
+
assert [1,3,2,3,1,:foo].to_ordered_set.is_a?(OrderedSet)
|
7
|
+
assert OrderedSet.new([1,3,2,3,1,:foo]).is_a?(OrderedSet)
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'be enumerable' do
|
11
|
+
assert_equal [nil,1], [nil,1,1,nil].to_ordered_set.collect {|i| i}
|
12
|
+
assert_equal [1,3,2,:foo], [1,3,2,3,1,:foo].to_ordered_set.collect {|i| i}
|
13
|
+
assert_equal [-1,-3,-12], [1,3,12,3,1].to_ordered_set.collect {|i| -i}
|
14
|
+
assert_equal [1], [1,1,0,1,0,0,0,1].to_ordered_set.select {|i| i != 0}
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'replace the set contents' do
|
18
|
+
set = [].to_ordered_set
|
19
|
+
set.replace([5,0,5])
|
20
|
+
|
21
|
+
assert_equal [5,0], set.to_a
|
22
|
+
assert_consistent set
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'join elements in set' do
|
26
|
+
set = ["five","O","five"].to_ordered_set
|
27
|
+
assert_equal 'five-O', set.join('-')
|
28
|
+
set << 5
|
29
|
+
assert_equal 'five-O-5', set.join('-')
|
30
|
+
assert_consistent set
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'add sets' do
|
34
|
+
set = [1,3,4].to_ordered_set + [2,3,5].to_ordered_set
|
35
|
+
|
36
|
+
assert_equal [1,3,4,2,5], set.to_a
|
37
|
+
assert_consistent set
|
38
|
+
end
|
39
|
+
|
40
|
+
should 'subtract sets' do
|
41
|
+
set = [5,1,3,4].to_ordered_set - [2,3,5].to_ordered_set
|
42
|
+
|
43
|
+
assert_equal [1,4], set.to_a
|
44
|
+
assert_consistent set
|
45
|
+
end
|
46
|
+
|
47
|
+
should 'intersect sets' do
|
48
|
+
set = [5,1,3,4].to_ordered_set & [2,3,5].to_ordered_set
|
49
|
+
|
50
|
+
assert_equal [5,3], set.to_a
|
51
|
+
assert_consistent set
|
52
|
+
end
|
53
|
+
|
54
|
+
should 'reorder set' do
|
55
|
+
set = [5,1,3,4].to_ordered_set
|
56
|
+
set.reorder!([2,3,5].to_ordered_set)
|
57
|
+
|
58
|
+
assert_equal [3,5,1,4], set.to_a
|
59
|
+
assert_consistent set
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'clear set' do
|
63
|
+
set = [1,2,3].to_ordered_set
|
64
|
+
|
65
|
+
set.clear
|
66
|
+
assert set.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
should 'clone set' do
|
70
|
+
set = [1,2,3].to_ordered_set
|
71
|
+
copy = set.clone
|
72
|
+
|
73
|
+
copy.clear
|
74
|
+
assert_equal [], copy.to_a
|
75
|
+
assert_equal [1,2,3], set.to_a
|
76
|
+
end
|
77
|
+
|
78
|
+
should 'include? all elements' do
|
79
|
+
set = [1,3,2,3,1,:foo,nil].to_ordered_set
|
80
|
+
|
81
|
+
assert set.include?(1)
|
82
|
+
assert set.include?(3)
|
83
|
+
assert set.include?(:foo)
|
84
|
+
assert set.include?(nil)
|
85
|
+
assert_consistent set
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'have the correct size and length' do
|
89
|
+
set = [1,1,1,1,2,2,1,2,3,4,2,1,0].to_ordered_set
|
90
|
+
assert_equal 5, set.size
|
91
|
+
assert_equal 5, set.length
|
92
|
+
assert_consistent set
|
93
|
+
end
|
94
|
+
|
95
|
+
should 'be empty' do
|
96
|
+
set = [].to_ordered_set
|
97
|
+
assert set.empty?
|
98
|
+
|
99
|
+
set = [1].to_ordered_set
|
100
|
+
assert !set.empty?
|
101
|
+
end
|
102
|
+
|
103
|
+
should 'check for any elements' do
|
104
|
+
set = [].to_ordered_set
|
105
|
+
assert !set.any?
|
106
|
+
|
107
|
+
set = [1].to_ordered_set
|
108
|
+
assert set.any?
|
109
|
+
end
|
110
|
+
|
111
|
+
should 'support index operator' do
|
112
|
+
set = [1,2,2,3,4,1,0].to_ordered_set
|
113
|
+
|
114
|
+
assert_equal 1, set[0]
|
115
|
+
assert_equal 4, set[3]
|
116
|
+
assert_equal nil, set[100]
|
117
|
+
assert_equal [2,3], set[1,2]
|
118
|
+
assert_equal [3,4,0], set[2..4]
|
119
|
+
assert_consistent set
|
120
|
+
end
|
121
|
+
|
122
|
+
should 'get a slice from a set' do
|
123
|
+
set = [1,2,2,3,4,1,0].to_ordered_set
|
124
|
+
|
125
|
+
assert_equal 1, set.slice(0)
|
126
|
+
assert_equal 4, set.slice(3)
|
127
|
+
assert_equal nil, set.slice(100)
|
128
|
+
assert_equal [2,3], set.slice(1,2)
|
129
|
+
assert_equal [3,4,0], set.slice(2..4)
|
130
|
+
assert_consistent set
|
131
|
+
end
|
132
|
+
|
133
|
+
should 'lookup index of element' do
|
134
|
+
set = [1,2,2,3,4,1,0].to_ordered_set
|
135
|
+
|
136
|
+
assert_equal 1, set.index(2)
|
137
|
+
assert_equal 4, set.index(0)
|
138
|
+
assert_consistent set
|
139
|
+
end
|
140
|
+
|
141
|
+
should 'append elements' do
|
142
|
+
set = [1,2,2,3,4,1,0].to_ordered_set
|
143
|
+
|
144
|
+
set << 1
|
145
|
+
assert_equal 5, set.size
|
146
|
+
assert set.include?(1)
|
147
|
+
assert_equal [1,2,3,4,0], set.to_a
|
148
|
+
assert_consistent set
|
149
|
+
|
150
|
+
set << 100
|
151
|
+
assert_equal 6, set.size
|
152
|
+
assert set.include?(100)
|
153
|
+
assert_equal [1,2,3,4,0,100], set.to_a
|
154
|
+
assert_consistent set
|
155
|
+
end
|
156
|
+
|
157
|
+
should 'unshift elements' do
|
158
|
+
set = [1,2,2,3,4,1,0].to_ordered_set
|
159
|
+
|
160
|
+
set.unshift(1)
|
161
|
+
assert_equal 5, set.size
|
162
|
+
assert set.include?(1)
|
163
|
+
assert_equal [1,2,3,4,0], set.to_a
|
164
|
+
assert_consistent set
|
165
|
+
|
166
|
+
set.unshift(100)
|
167
|
+
assert_equal 6, set.size
|
168
|
+
assert set.include?(100)
|
169
|
+
assert_equal [100,1,2,3,4,0], set.to_a
|
170
|
+
assert_consistent set
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
should 'delete elements' do
|
175
|
+
set = [1,2,2,3,4,1,nil].to_ordered_set
|
176
|
+
|
177
|
+
assert_equal 1, set.delete(1)
|
178
|
+
assert_equal [2,3,4,nil], set.to_a
|
179
|
+
assert_equal 4, set.size
|
180
|
+
assert !set.include?(1)
|
181
|
+
assert_consistent set
|
182
|
+
|
183
|
+
assert_equal nil, set.delete(nil)
|
184
|
+
assert_equal [2,3,4], set.to_a
|
185
|
+
assert_equal 3, set.size
|
186
|
+
assert !set.include?(nil)
|
187
|
+
assert_consistent set
|
188
|
+
|
189
|
+
assert_equal false, set.delete(100) { false }
|
190
|
+
assert_equal [2,3,4], set.to_a
|
191
|
+
assert_equal 3, set.size
|
192
|
+
assert !set.include?(100)
|
193
|
+
assert_consistent set
|
194
|
+
end
|
195
|
+
|
196
|
+
should 'delete elements at specified position' do
|
197
|
+
set = [1,2,2,3,4,1,nil].to_ordered_set
|
198
|
+
|
199
|
+
assert_equal 1, set.delete_at(0)
|
200
|
+
assert_equal [2,3,4,nil], set.to_a
|
201
|
+
assert_equal 4, set.size
|
202
|
+
assert !set.include?(1)
|
203
|
+
assert_consistent set
|
204
|
+
|
205
|
+
assert_equal nil, set.delete_at(3)
|
206
|
+
assert_equal [2,3,4], set.to_a
|
207
|
+
assert_equal 3, set.size
|
208
|
+
assert !set.include?(nil)
|
209
|
+
assert_consistent set
|
210
|
+
|
211
|
+
assert_equal false, set.delete(100) { false }
|
212
|
+
assert_equal [2,3,4], set.to_a
|
213
|
+
assert_equal 3, set.size
|
214
|
+
assert !set.include?(100)
|
215
|
+
assert_consistent set
|
216
|
+
end
|
217
|
+
|
218
|
+
should 'sort set' do
|
219
|
+
set = [1,2,2,3,4,1,0].to_ordered_set
|
220
|
+
|
221
|
+
set.sort!
|
222
|
+
assert_equal [0,1,2,3,4], set.to_a
|
223
|
+
assert_consistent set
|
224
|
+
end
|
225
|
+
|
226
|
+
should 'reverse set' do
|
227
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set
|
228
|
+
|
229
|
+
set.reverse!
|
230
|
+
assert_equal [4,3,2,1,0], set.to_a
|
231
|
+
assert_consistent set
|
232
|
+
end
|
233
|
+
|
234
|
+
should 'modify using collect!' do
|
235
|
+
set = [0,1,2,2,3,4,1,0,5,6,8,9,11,32424].to_ordered_set
|
236
|
+
|
237
|
+
set.collect! {|i| i % 3}
|
238
|
+
assert_equal [0,1,2], set.to_a
|
239
|
+
assert_consistent set
|
240
|
+
end
|
241
|
+
|
242
|
+
should 'modify using map!' do
|
243
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set
|
244
|
+
|
245
|
+
set.collect! {|i| i * 10}
|
246
|
+
assert_equal [0,10,20,30,40], set.to_a
|
247
|
+
assert_consistent set
|
248
|
+
end
|
249
|
+
|
250
|
+
should 'compact set' do
|
251
|
+
set = [0,1,2,2,nil,3,nil,4,1,0].to_ordered_set
|
252
|
+
|
253
|
+
set.compact!
|
254
|
+
assert_equal [0,1,2,3,4], set.to_a
|
255
|
+
assert_consistent set
|
256
|
+
end
|
257
|
+
|
258
|
+
should 'select elements from set' do
|
259
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set
|
260
|
+
|
261
|
+
set.select! {|i| i % 2 == 0}
|
262
|
+
assert_equal [0,2,4], set.to_a
|
263
|
+
assert_consistent set
|
264
|
+
end
|
265
|
+
|
266
|
+
should 'reject elements from set' do
|
267
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set
|
268
|
+
|
269
|
+
set.reject! {|i| i % 2 == 0}
|
270
|
+
assert_equal [1,3], set.to_a
|
271
|
+
assert_consistent set
|
272
|
+
end
|
273
|
+
|
274
|
+
should 'modify set with slice!' do
|
275
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set
|
276
|
+
|
277
|
+
assert_equal [1,2,3], set.slice!(1..3)
|
278
|
+
assert_equal [0,4], set.to_a
|
279
|
+
assert_consistent set
|
280
|
+
end
|
281
|
+
|
282
|
+
should 'limit set' do
|
283
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set.limit!(3, 1)
|
284
|
+
|
285
|
+
assert_equal [1,2,3], set.to_a
|
286
|
+
assert_consistent set
|
287
|
+
|
288
|
+
set = [0,1,2,2,3,4,1,0].to_ordered_set.limit!(2)
|
289
|
+
|
290
|
+
assert_equal [0,1], set.to_a
|
291
|
+
assert_consistent set
|
292
|
+
end
|
293
|
+
|
294
|
+
def assert_consistent(set)
|
295
|
+
set.each do |item|
|
296
|
+
assert set.include?(item)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
7
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../../deep_clonable/lib"
|
8
|
+
require 'ordered_set'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ninjudd-ordered_set
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Balthrop
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-13 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Like Set except it maintains the order of objects
|
17
|
+
email: code@justinbalthrop.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README.rdoc
|
26
|
+
- VERSION.yml
|
27
|
+
- lib/ordered_set.rb
|
28
|
+
- test/ordered_set_test.rb
|
29
|
+
- test/test_helper.rb
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://github.com/ninjudd/ordered_set
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options:
|
34
|
+
- --inline-source
|
35
|
+
- --charset=UTF-8
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: "0"
|
43
|
+
version:
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.2.0
|
54
|
+
signing_key:
|
55
|
+
specification_version: 2
|
56
|
+
summary: Like Set except it maintains the order of objects
|
57
|
+
test_files: []
|
58
|
+
|