multiset 0.3.0 → 0.4.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/.autotest +23 -0
- data/.gemtest +0 -0
- data/History.txt +206 -0
- data/README.txt +84 -0
- data/Rakefile +17 -44
- data/lib/multimap.rb +480 -0
- data/lib/multiset.rb +1135 -121
- data/spec/multiset_spec.rb +619 -2
- data/spec/spec_helper.rb +0 -8
- metadata +68 -118
- data/.document +0 -5
- data/.rspec +0 -1
- data/Gemfile +0 -13
- data/LICENSE.txt +0 -20
- data/README.rdoc +0 -19
- data/VERSION +0 -1
- data/lib/multiset/libmultimap.rb +0 -475
- data/lib/multiset/libmultiset.rb +0 -757
- data/multiset.gemspec +0 -66
- data/spec/multiset_spec_ported.rb +0 -528
data/spec/multiset_spec.rb
CHANGED
@@ -1,12 +1,629 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
#
|
4
|
-
# Unit test code will be added from the next version.
|
3
|
+
# Note: NOT ALL METHODS ARE TESTED!
|
5
4
|
|
6
5
|
require "multiset"
|
7
6
|
|
8
7
|
describe Multiset do
|
8
|
+
# Generation methods
|
9
|
+
describe "being generated" do
|
10
|
+
describe "by Multiset.parse(Hash)" do
|
11
|
+
it "should regard keys as elements and values as counts" do
|
12
|
+
Multiset.parse({:a => 3, :b => 2}).should == Multiset[:a, :a, :a, :b, :b]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "by Multiset.parse(NonHashEnumerable)" do
|
17
|
+
it "should regard each of the elements as an element in the Multiset" do
|
18
|
+
Multiset.parse([:a, 3, :b, 2]).should == Multiset[:a, 3, :b, 2]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "by Multiset.parse(NonEnumerable)" do
|
23
|
+
it "should be rejected" do
|
24
|
+
lambda{ Multiset.parse(83) }.should raise_error(ArgumentError)
|
25
|
+
lambda{ Multiset.parse("hoge\npiyo\n") }.should raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "by Multiset.parse_force(NonEnumerable)" do
|
30
|
+
it "should be rejected except for strings" do
|
31
|
+
lambda{ Multiset.parse_force(83) }.should raise_error(ArgumentError)
|
32
|
+
Multiset.parse_force("hoge\npiyo\n").should == Multiset["hoge\n", "piyo\n"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "by Multiset#dup" do
|
37
|
+
it "should be the same as the original Multiset" do
|
38
|
+
tmp = Multiset.new([:a,:a,:b])
|
39
|
+
tmp.should == tmp.dup
|
40
|
+
|
41
|
+
tmp = Multiset[]
|
42
|
+
tmp.should == tmp.dup
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be replaced by another Multiset by Multiset#replace" do
|
48
|
+
tmp = Multiset[]
|
49
|
+
tmp.replace(Multiset[:a,:b,:b]).should == Multiset[:a,:b,:b]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Comparatory/merging methods
|
53
|
+
describe "of two" do
|
54
|
+
before do
|
55
|
+
@ms1 = Multiset.new(%w'a a a a b b b c c d')
|
56
|
+
@ms2 = Multiset.new(%w'b c c d d d e e e e')
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should judge inclusions correctly" do
|
60
|
+
tmp1 = Multiset.new(%w'a a a a b b b c c d d')
|
61
|
+
tmp2 = Multiset.new(%w'a a a a b b b c c d')
|
62
|
+
tmp3 = Multiset.new(%w'a a a b b b c c d')
|
63
|
+
|
64
|
+
(@ms1 == @ms2).should be_false
|
65
|
+
(@ms1 == tmp1).should be_false
|
66
|
+
(@ms1 == tmp2).should be_true
|
67
|
+
(@ms1 == tmp3).should be_false
|
68
|
+
|
69
|
+
(@ms1.subset?(@ms2)).should be_false
|
70
|
+
(@ms1.subset?(tmp1)).should be_true
|
71
|
+
(@ms1.subset?(tmp2)).should be_true
|
72
|
+
(@ms1.subset?(tmp3)).should be_false
|
73
|
+
(@ms2.subset?(@ms1)).should be_false
|
74
|
+
(tmp1.subset?(@ms1)).should be_false
|
75
|
+
(tmp2.subset?(@ms1)).should be_true
|
76
|
+
(tmp3.subset?(@ms1)).should be_true
|
77
|
+
|
78
|
+
(@ms1.proper_subset?(@ms2)).should be_false
|
79
|
+
(@ms1.proper_subset?(tmp1)).should be_true
|
80
|
+
(@ms1.proper_subset?(tmp2)).should be_false
|
81
|
+
(@ms1.proper_subset?(tmp3)).should be_false
|
82
|
+
(@ms2.proper_subset?(@ms1)).should be_false
|
83
|
+
(tmp1.proper_subset?(@ms1)).should be_false
|
84
|
+
(tmp2.proper_subset?(@ms1)).should be_false
|
85
|
+
(tmp3.proper_subset?(@ms1)).should be_true
|
86
|
+
|
87
|
+
(@ms1.superset?(@ms2)).should be_false
|
88
|
+
(@ms1.superset?(tmp1)).should be_false
|
89
|
+
(@ms1.superset?(tmp2)).should be_true
|
90
|
+
(@ms1.superset?(tmp3)).should be_true
|
91
|
+
(@ms2.superset?(@ms1)).should be_false
|
92
|
+
(tmp1.superset?(@ms1)).should be_true
|
93
|
+
(tmp2.superset?(@ms1)).should be_true
|
94
|
+
(tmp3.superset?(@ms1)).should be_false
|
95
|
+
|
96
|
+
(@ms1.proper_superset?(@ms2)).should be_false
|
97
|
+
(@ms1.proper_superset?(tmp1)).should be_false
|
98
|
+
(@ms1.proper_superset?(tmp2)).should be_false
|
99
|
+
(@ms1.proper_superset?(tmp3)).should be_true
|
100
|
+
(@ms2.proper_superset?(@ms1)).should be_false
|
101
|
+
(tmp1.proper_superset?(@ms1)).should be_true
|
102
|
+
(tmp2.proper_superset?(@ms1)).should be_false
|
103
|
+
(tmp3.proper_superset?(@ms1)).should be_false
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should compute the intersection correctly" do
|
107
|
+
(@ms1 & @ms2).should == Multiset.new(%w'b c c d')
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should compute the union correctly" do
|
111
|
+
(@ms1 | @ms2).should == Multiset.new(%w'a a a a b b b c c d d d e e e e')
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should compute the sum correctly" do
|
115
|
+
(@ms1 + @ms2).should == Multiset.new(%w'a a a a b b b b c c c c d d d d e e e e')
|
116
|
+
|
117
|
+
tmp = @ms1.dup
|
118
|
+
tmp.merge!(@ms2)
|
119
|
+
tmp.should == Multiset.new(%w'a a a a b b b b c c c c d d d d e e e e')
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should compute the difference correctly" do
|
123
|
+
(@ms1 - @ms2).should == Multiset.new(%w'a a a a b b')
|
124
|
+
|
125
|
+
tmp = @ms1.dup
|
126
|
+
tmp.subtract!(@ms2)
|
127
|
+
tmp.should == Multiset.new(%w'a a a a b b')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Iteration methods
|
132
|
+
describe "being iterated for entries" do
|
133
|
+
before do
|
134
|
+
@ms = Multiset.new(%w'a a b b b b c d d d')
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should have the same result between Multiset\#{all?, any?, none?, one?} and Enumerable\#{all?, any?, none?, one?}" do
|
138
|
+
@ms.all?{ |x| x == "a" }.should be_false
|
139
|
+
@ms.any?{ |x| x == "a" }.should be_true
|
140
|
+
@ms.none?{ |x| x == "a" }.should be_false
|
141
|
+
@ms.none?{ |x| x.instance_of?(Integer) }.should be_true
|
142
|
+
@ms.one?{ |x| x == "a" }.should be_false
|
143
|
+
@ms.one?{ |x| x == "c" }.should be_true
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should have the same result between Multiset#count and Enumerable#count" do
|
147
|
+
@ms.count("a").should == 2
|
148
|
+
@ms.count("x").should == 0
|
149
|
+
@ms.count.should == 10
|
150
|
+
@ms.count{ |x| x == "b" }.should == 4
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should find an items by the specified condition with Multiset#find / Multiset#detect" do
|
154
|
+
@ms.find{ |item| item =~ /d/ }.should == "d"
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should find an item by the specified condition with Multiset#find_with / Multiset#detect_with" do
|
158
|
+
@ms.find_with{ |item, count| count == 2 }.should == "a"
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should find an items by the specified condition with Multiset#find_all / Multiset#select" do
|
162
|
+
@ms.find_all{ |item| item =~ /d/ }.should == Multiset.new(%w[d d d])
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should find an items by the specified condition with Multiset#find_all_with / Multiset#select_with" do
|
166
|
+
@ms.find_all_with{ |item, count| count <= 2 }.should == Multiset.new(%w[a a c])
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should find an items by the specified condition with Multiset#reject" do
|
170
|
+
@ms.reject{ |item| item =~ /d/ }.should == Multiset.new(%w[a a b b b b c])
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should find an items by the specified condition with Multiset#reject!" do
|
174
|
+
@ms.reject!{ |item| item =~ /x/ }.should be_nil
|
175
|
+
@ms.reject!{ |item| item =~ /d/ }.object_id.should == @ms.object_id
|
176
|
+
@ms.should == Multiset.new(%w[a a b b b b c])
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should find an items by the specified condition with Multiset#delete_if" do
|
180
|
+
@ms.delete_if{ |item| item =~ /x/ }.object_id.should == @ms.object_id
|
181
|
+
@ms.delete_if{ |item| item =~ /d/ }.object_id.should == @ms.object_id
|
182
|
+
@ms.should == Multiset.new(%w[a a b b b b c])
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should find an items by the specified condition with Multiset#reject_with" do
|
186
|
+
@ms.reject_with{ |item, count| item =~ /d/ || count > 3 }.should == Multiset.new(%w[a a c])
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should find an items by the specified condition with Multiset#grep" do
|
190
|
+
@ms.grep(/d/).should == Multiset.new(%w[d d d])
|
191
|
+
@ms.grep(/d/){ |item| item + "x" }.should == Multiset.new(%w[dx dx dx])
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should repeat for items by the specified condition with Multiset#inject_with / Multiset#reduce_with" do
|
195
|
+
result = @ms.inject_with({ :item_sum => "", :count_sum => 0 }) do |obj, item, count|
|
196
|
+
obj[:item_sum] += item
|
197
|
+
obj[:count_sum] += count
|
198
|
+
obj
|
199
|
+
end
|
200
|
+
result[:item_sum].each_char.sort.should == %w[a b c d]
|
201
|
+
result[:count_sum].should == 10
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should return a new Multiset by the specified condition with Multiset#map / Multiset#collect" do
|
205
|
+
@ms.map{ |item| item * 2 }.should == Multiset.new(%w'aa aa bb bb bb bb cc dd dd dd')
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should return a new Multiset by the specified condition with Multiset#map_with / Multiset#collect_with" do
|
209
|
+
@ms.map_with{ |item, count| [item * 2, count * 2] }.should == Multiset.new(%w'aa aa aa aa bb bb bb bb bb bb bb bb cc cc dd dd dd dd dd dd')
|
210
|
+
end
|
211
|
+
|
212
|
+
it "should return the maximum/mininum value by max/min/minmax" do
|
213
|
+
@ms.max.should == "d"
|
214
|
+
@ms.min.should == "a"
|
215
|
+
@ms.minmax.should == %w[a d]
|
216
|
+
@ms.max{ |a, b| b <=> a }.should == "a"
|
217
|
+
@ms.min{ |a, b| b <=> a }.should == "d"
|
218
|
+
@ms.minmax{ |a, b| b <=> a }.should == %w[d a]
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should return the maximum/mininum value by max_by/min_by/minmax_by" do
|
222
|
+
@ms.max_by{ |item| item }.should == "d"
|
223
|
+
@ms.min_by{ |item| item }.should == "a"
|
224
|
+
@ms.minmax_by{ |item| item }.should == %w[a d]
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should return the maximum/mininum value by max_with/min_with/minmax_with" do
|
228
|
+
@ms.max_with{ |a_item, a_count, b_item, b_count| a_count <=> b_count }.should == "b"
|
229
|
+
@ms.max_with{ |a_item, a_count, b_item, b_count| b_count <=> a_count }.should == "c"
|
230
|
+
@ms.min_with{ |a_item, a_count, b_item, b_count| a_count <=> b_count }.should == "c"
|
231
|
+
@ms.min_with{ |a_item, a_count, b_item, b_count| b_count <=> a_count }.should == "b"
|
232
|
+
@ms.minmax_with{ |a_item, a_count, b_item, b_count| a_count <=> b_count }.should == %w[c b]
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should return the maximum/mininum value by max_by_with/min_by_with/minmax_by_with" do
|
236
|
+
@ms.max_by_with{ |item, count| count }.should == "b"
|
237
|
+
@ms.min_by_with{ |item, count| count }.should == "c"
|
238
|
+
@ms.minmax_by_with{ |item, count| count }.should == %w[c b]
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should return sorted array by Multiset#sort / Multiset#sort_with" do
|
242
|
+
@ms.sort.should == %w[a a b b b b c d d d]
|
243
|
+
@ms.sort{ |a, b| b <=> a }.should == %w[d d d c b b b b a a]
|
244
|
+
@ms.sort_with{ |a_item, a_count, b_item, b_count| b_count <=> a_count }.should == %w[b b b b d d d a a c]
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should return sorted array by Multiset#sort_by / Multiset#sort_by_with" do
|
248
|
+
@ms.sort_by{ |item| item }.should == %w[a a b b b b c d d d]
|
249
|
+
@ms.sort_by_with{ |item, count| count }.should == %w[c a a d d d b b b b]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Updating methods
|
254
|
+
describe "being updated" do
|
255
|
+
before do
|
256
|
+
@ms1 = Multiset.new(%w'a a a a b b b c c d')
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should add an element correctly" do
|
260
|
+
tmp = @ms1.dup
|
261
|
+
tmp << "a"
|
262
|
+
tmp.should == Multiset.new(%w'a a a a a b b b c c d')
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should add multiple element correctly" do
|
266
|
+
tmp = @ms1.dup
|
267
|
+
tmp.add("e", 3)
|
268
|
+
tmp.should == Multiset.new(%w'a a a a b b b c c d e e e')
|
269
|
+
|
270
|
+
tmp = @ms1.dup
|
271
|
+
tmp.add("a", 3)
|
272
|
+
tmp.should == Multiset.new(%w'a a a a a a a b b b c c d')
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should remove an element correctly" do
|
276
|
+
tmp = @ms1.dup
|
277
|
+
tmp.delete("a")
|
278
|
+
tmp.should == Multiset.new(%w'a a a b b b c c d')
|
279
|
+
|
280
|
+
tmp = @ms1.dup
|
281
|
+
tmp.delete("e") # nothing deleted because `tmp' does not contain "e"
|
282
|
+
tmp.should == @ms1
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should remove multiple element correctly" do
|
286
|
+
tmp = @ms1.dup
|
287
|
+
tmp.delete("a", 3)
|
288
|
+
tmp.should == Multiset.new(%w'a b b b c c d')
|
289
|
+
|
290
|
+
tmp = @ms1.dup
|
291
|
+
tmp.delete("a", 6) # in case `tmp' does not contain "a" less than 6 times
|
292
|
+
tmp.should == Multiset.new(%w'b b b c c d')
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should remove all element of specified value correctly" do
|
296
|
+
tmp = @ms1.dup
|
297
|
+
tmp.delete_all("a")
|
298
|
+
tmp.should == Multiset.new(%w'b b b c c d')
|
299
|
+
|
300
|
+
tmp = @ms1.dup
|
301
|
+
tmp.delete_all("e") # nothing deleted because `tmp' does not contain "e"
|
302
|
+
tmp.should == @ms1
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe Hash, "when converted to a multiset" do
|
308
|
+
it "should regard keys as elements and values as counts by Hash#to_multiset" do
|
309
|
+
{:a => 2, :b => 1}.to_multiset.should == Multiset[:a,:a,:b]
|
310
|
+
{:x => 2, :y => 2, :z => 0}.to_multiset.should == Multiset[:x,:x,:y,:y]
|
311
|
+
end
|
9
312
|
end
|
10
313
|
|
11
314
|
describe Multimap do
|
315
|
+
describe "generated by adding items one by one" do
|
316
|
+
before do
|
317
|
+
@empty_multiset = Multiset.new
|
318
|
+
@mm = Multimap.new
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should be empty for any key when generated without parameters" do
|
322
|
+
@mm[:a].should == @empty_multiset
|
323
|
+
@mm[:b].should == @empty_multiset
|
324
|
+
end
|
325
|
+
|
326
|
+
it "should be a singleton multiset by adding a scalar value" do
|
327
|
+
@mm[:a].add "bar"
|
328
|
+
@mm[:a].should == Multiset["bar"]
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should not make any unsolicited change" do
|
332
|
+
@mm[:a].add "bar"
|
333
|
+
@mm[:b].should == @empty_multiset
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should be a multiset constructed by the given array" do
|
337
|
+
@mm[:a] = %w[foo foo bar]
|
338
|
+
@mm[:a].should == Multiset.new(%w[foo foo bar])
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should be a multiset constructed by the given hash (key: element, value: count)" do
|
342
|
+
@mm[:a] = {"foo" => 3, "bar" => 2}
|
343
|
+
@mm[:a].should == Multiset["foo", "foo", "bar", "foo", "bar"]
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should not be updated by setting a scalar (other than 'Enumerable' object)" do
|
347
|
+
lambda{ @mm[:a] = "foobar" }.should raise_error(ArgumentError)
|
348
|
+
lambda{ @mm[:a] = 56 }.should raise_error(ArgumentError)
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should be duplicated by Multimap#dup" do
|
352
|
+
@mm.dup.should == @mm
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
describe "being compared" do
|
357
|
+
it "should be correctly compared" do
|
358
|
+
mm1 = Multimap.new
|
359
|
+
mm1[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
360
|
+
mm1[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
|
361
|
+
mm2 = Multimap.new
|
362
|
+
mm2[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
363
|
+
mm2[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
|
364
|
+
mm3 = Multimap.new
|
365
|
+
mm3[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
366
|
+
mm3[:b] = ["foo", "bar", "hoge", "hoge", "bar", "buz"]
|
367
|
+
|
368
|
+
mm1.should == mm2
|
369
|
+
mm1.should_not == mm3
|
370
|
+
|
371
|
+
mm3.dup.should == mm3
|
372
|
+
end
|
373
|
+
|
374
|
+
it "should be correctly decided whether it is empty or not" do
|
375
|
+
mm = Multimap.new
|
376
|
+
|
377
|
+
mm[:a] = []
|
378
|
+
mm[:b] = [:a, :b]
|
379
|
+
mm.should_not be_empty
|
380
|
+
|
381
|
+
mm[:a] = []
|
382
|
+
mm[:b] = []
|
383
|
+
mm.should be_empty
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe "being referred" do
|
388
|
+
before do
|
389
|
+
@mm = Multimap.new
|
390
|
+
@mm[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
391
|
+
@mm[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
|
392
|
+
end
|
393
|
+
|
394
|
+
it "should be correctly iterated by 'each_pair'" do
|
395
|
+
tmp_a = []
|
396
|
+
tmp_b = []
|
397
|
+
@mm.each_pair do |key, sval|
|
398
|
+
case key
|
399
|
+
when :a
|
400
|
+
tmp_a << sval
|
401
|
+
when :b
|
402
|
+
tmp_b << sval
|
403
|
+
else
|
404
|
+
raise
|
405
|
+
end
|
406
|
+
end
|
407
|
+
tmp_a.sort.should == ["foo", "foo", "bar", "hoge", "hoge", "moe"].sort
|
408
|
+
tmp_b.sort.should == ["foo", "bar", "hoge", "hoge", "bar"].sort
|
409
|
+
end
|
410
|
+
|
411
|
+
it "should be correctly iterated by 'each_pair_with'" do
|
412
|
+
tmp_a = []
|
413
|
+
tmp_b = []
|
414
|
+
@mm.each_pair_with do |key, val, cnt|
|
415
|
+
case key
|
416
|
+
when :a
|
417
|
+
tmp_a << val
|
418
|
+
when :b
|
419
|
+
tmp_b << val
|
420
|
+
else
|
421
|
+
raise
|
422
|
+
end
|
423
|
+
end
|
424
|
+
tmp_a.sort.should == ["foo", "foo", "bar", "hoge", "hoge", "moe"].uniq.sort
|
425
|
+
tmp_b.sort.should == ["foo", "bar", "hoge", "hoge", "bar"].uniq.sort
|
426
|
+
end
|
427
|
+
|
428
|
+
it "should be correctly iterated by 'each_pair_list'" do
|
429
|
+
flag = 0
|
430
|
+
@mm.each_pair_list do |key, vals|
|
431
|
+
case key
|
432
|
+
when :a
|
433
|
+
vals.should == Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
434
|
+
flag += 1
|
435
|
+
when :b
|
436
|
+
vals.should == Multiset["foo", "bar", "hoge", "hoge", "bar"]
|
437
|
+
flag += 1
|
438
|
+
else
|
439
|
+
raise
|
440
|
+
end
|
441
|
+
end
|
442
|
+
flag.should == 2
|
443
|
+
end
|
444
|
+
|
445
|
+
it "should ignore non-existent key by 'each_key'" do
|
446
|
+
@mm[:c] = []
|
447
|
+
|
448
|
+
ms = Multiset.new
|
449
|
+
@mm.each_key{ |key| ms << key }
|
450
|
+
ms.should == Multiset[:a, :b]
|
451
|
+
end
|
452
|
+
|
453
|
+
it "should be correctly iterated by 'each_value'" do
|
454
|
+
ms = Multiset.new
|
455
|
+
@mm.each_value{ |sval| ms << sval }
|
456
|
+
ms.should == {"foo" => 3, "bar" => 3, "hoge" => 4, "moe" => 1}.to_multiset
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
describe "being updated" do
|
461
|
+
before do
|
462
|
+
@mm = Multimap.new
|
463
|
+
@mm[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
464
|
+
@mm[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
|
465
|
+
end
|
466
|
+
|
467
|
+
it "should release items when 'delete'd" do
|
468
|
+
@mm.delete(:a).should == Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
469
|
+
@mm[:a].should == Multiset[]
|
470
|
+
@mm[:b].should == Multiset["foo", "bar", "hoge", "hoge", "bar"]
|
471
|
+
end
|
472
|
+
|
473
|
+
it "should not release items when 'reject'ed ('reject'ed from only return value)" do
|
474
|
+
mm1 = @mm.reject{ |key, sval| sval =~ /o/ }
|
475
|
+
|
476
|
+
mm1[:a].should == Multiset["bar"]
|
477
|
+
mm1[:b].should == Multiset["bar", "bar"]
|
478
|
+
mm1.should_not == @mm
|
479
|
+
end
|
480
|
+
|
481
|
+
it "should equal to 'reject!'ed multimap when 'delete_if' is applied" do
|
482
|
+
mm1 = @mm.dup
|
483
|
+
mm2 = @mm.dup
|
484
|
+
|
485
|
+
mm1.delete_if{ |key, sval| sval =~ /o/ }
|
486
|
+
mm2.reject!{ |key, sval| sval =~ /o/ }
|
487
|
+
mm1.should == mm2
|
488
|
+
|
489
|
+
mm1.delete_if{ |key, sval| sval =~ /x/ }
|
490
|
+
mm2.reject!{ |key, sval| sval =~ /x/ }
|
491
|
+
mm1.should == mm2
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should return nil when 'reject!' rejects nothing but 'delete_if' not" do
|
495
|
+
mm1 = @mm.dup
|
496
|
+
mm2 = @mm.dup
|
497
|
+
|
498
|
+
mm1.delete_if{ |key, sval| sval =~ /o/ }.should == mm1
|
499
|
+
mm2.reject!{ |key, sval| sval =~ /o/ }.should == mm2
|
500
|
+
|
501
|
+
mm1.delete_if{ |key, sval| sval =~ /x/ }.should == mm1
|
502
|
+
mm2.reject!{ |key, sval| sval =~ /x/ }.should == nil
|
503
|
+
end
|
504
|
+
|
505
|
+
it "should not release items when 'reject_with' is applied ('reject'ed from only return value)" do
|
506
|
+
# reject_with
|
507
|
+
mm1 = @mm.reject_with{ |key, val, cnt| cnt >= 2 }
|
508
|
+
|
509
|
+
mm1[:a].should == Multiset["bar", "moe"]
|
510
|
+
mm1[:b].should == Multiset["foo"]
|
511
|
+
mm1.should_not == @mm
|
512
|
+
end
|
513
|
+
|
514
|
+
it "should release items when 'delete_with' is applied" do
|
515
|
+
retval = @mm.delete_with{ |key, val, cnt| cnt >= 2 }
|
516
|
+
retval.should == @mm
|
517
|
+
@mm[:a].should == Multiset["bar", "moe"]
|
518
|
+
@mm[:b].should == Multiset["foo"]
|
519
|
+
end
|
520
|
+
|
521
|
+
it "should release items when 'delete_with' is applied (but not deleted anything)" do
|
522
|
+
retval = @mm.delete_with{ |key, val, cnt| cnt >= 2 && val.length > 4 }
|
523
|
+
retval.should == @mm
|
524
|
+
@mm[:a].should == Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
525
|
+
@mm[:b].should == Multiset["foo", "bar", "hoge", "hoge", "bar"]
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
describe "being checked its properties" do
|
530
|
+
before do
|
531
|
+
@mm_base = Multimap.new
|
532
|
+
@mm_base[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
|
533
|
+
@mm_base[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
|
534
|
+
end
|
535
|
+
|
536
|
+
# keys, values
|
537
|
+
it "should return existing (nonzero) keys by Multimap#keys" do
|
538
|
+
mm = @mm_base.dup
|
539
|
+
mm[:c] = []
|
540
|
+
mm.keys.should satisfy{ |ks| ks == [:a, :b] || ks == [:b, :a] }
|
541
|
+
end
|
542
|
+
|
543
|
+
it "should return all values by Multimap#values" do
|
544
|
+
@mm_base.values.should == {"foo" => 3, "bar" => 3, "hoge" => 4, "moe" => 1}.to_multiset
|
545
|
+
end
|
546
|
+
|
547
|
+
# empty?
|
548
|
+
it "should return true by Multimap#empty? if and only if it is empty" do
|
549
|
+
mm = @mm_base.dup
|
550
|
+
mm[:a] = []
|
551
|
+
mm.delete :b
|
552
|
+
mm.should be_empty
|
553
|
+
|
554
|
+
mm[:c] = [:x]
|
555
|
+
mm.should_not be_empty
|
556
|
+
end
|
557
|
+
|
558
|
+
# has_key?
|
559
|
+
it "should return true by Multimap#has_key? if and only if it has the given key" do
|
560
|
+
mm = @mm_base.dup
|
561
|
+
mm[:c] = []
|
562
|
+
mm[:d] = [:z]
|
563
|
+
mm.should have_key(:a)
|
564
|
+
mm.should have_key(:b)
|
565
|
+
mm.should_not have_key(:c)
|
566
|
+
mm.should be_member(:d)
|
567
|
+
mm.should_not be_member(:x)
|
568
|
+
end
|
569
|
+
|
570
|
+
# has_value?
|
571
|
+
it "should return true by Multimap#has_value? if and only if it has the given value" do
|
572
|
+
mm = @mm_base.dup
|
573
|
+
mm[:c] = []
|
574
|
+
mm[:d] = [:z]
|
575
|
+
mm.should have_value("foo")
|
576
|
+
mm.should have_value(:z)
|
577
|
+
mm.should_not have_value("boo")
|
578
|
+
mm.should be_value("hoge")
|
579
|
+
mm.should_not be_value(:d)
|
580
|
+
end
|
581
|
+
|
582
|
+
# index
|
583
|
+
it "should find a value and return a key by Multimap#index" do
|
584
|
+
mm = @mm_base.dup
|
585
|
+
mm.index("moe").should == :a
|
586
|
+
[:a, :b].should be_member(mm.index("hoge"))
|
587
|
+
mm.index("boo").should == nil
|
588
|
+
end
|
589
|
+
|
590
|
+
# values_at
|
591
|
+
it "should find a corresponding values as multisets by Multimap#values_at" do
|
592
|
+
mm = @mm_base.dup
|
593
|
+
mm[:x] = []
|
594
|
+
|
595
|
+
mm.values_at(:b, :x, :a).should == [
|
596
|
+
Multiset["foo", "bar", "hoge", "hoge", "bar"],
|
597
|
+
Multiset[],
|
598
|
+
Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"],
|
599
|
+
]
|
600
|
+
|
601
|
+
mm.indexes(:b, :x, :a).should == mm.values_at(:b, :x, :a)
|
602
|
+
end
|
603
|
+
|
604
|
+
# length, size
|
605
|
+
it "should return the collect number of entries by Multimap#length/size" do
|
606
|
+
mm = @mm_base.dup
|
607
|
+
mm.length.should == 11
|
608
|
+
mm[:c] = [:y]
|
609
|
+
mm.length.should == 12
|
610
|
+
mm.length.should == mm.size
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
describe Hash, "when converted to a multimap" do
|
616
|
+
it "should regard keys as multimap keys and values as multisets by Hash#to_multimap" do
|
617
|
+
# to_multimap / multimap
|
618
|
+
mm = {:a => [3, 3], :b => [5, 3]}.to_multimap
|
619
|
+
mm[:a].should == Multiset[3, 3]
|
620
|
+
mm[:b].should == Multiset[5, 3]
|
621
|
+
end
|
622
|
+
|
623
|
+
it "should regard keys as multimap keys and values as multisets by Hash#multimap" do
|
624
|
+
mm = {:a => [3, 3], :b => [5, 3]}.multimap
|
625
|
+
mm[:a].should == Multiset[[3, 3]]
|
626
|
+
mm[:b].should == Multiset[[5, 3]]
|
627
|
+
end
|
12
628
|
end
|
629
|
+
|