quality_extensions 1.1.4 → 1.1.6
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/lib/Xfind_bug_test.rb +28 -0
- data/lib/quality_extensions/array/all_same.rb +40 -0
- data/lib/quality_extensions/array/delete_if_bang.rb +145 -0
- data/lib/quality_extensions/array/expand_ranges.rb +3 -53
- data/lib/quality_extensions/array/include_any_of.rb +23 -0
- data/lib/quality_extensions/array/justify.rb +305 -0
- data/lib/quality_extensions/array/{average.rb → mean.rb} +1 -1
- data/lib/quality_extensions/array/select_if_bang.rb +0 -0
- data/lib/quality_extensions/array/sum.rb +30 -0
- data/lib/quality_extensions/enumerable/all_same.rb +43 -0
- data/lib/quality_extensions/enumerable/group_by_and_map.rb +110 -0
- data/lib/quality_extensions/enumerable/select_bang.rb +49 -0
- data/lib/quality_extensions/enumerable/select_while.rb +337 -52
- data/lib/quality_extensions/enumerable/select_with_index.rb +145 -0
- data/lib/quality_extensions/hash/hash_select.rb +5 -2
- data/lib/quality_extensions/hash/merge_if.rb +48 -0
- data/lib/quality_extensions/kernel/example_printer.rb +10 -3
- data/lib/quality_extensions/kernel/require_all.rb +24 -8
- data/lib/quality_extensions/kernel/sleep_loudly.rb +108 -0
- data/lib/quality_extensions/kernel/uninterruptable.rb +22 -0
- data/lib/quality_extensions/matrix/indexable.rb +68 -0
- data/lib/quality_extensions/matrix/linked_vectors.rb +137 -0
- data/lib/quality_extensions/module/class_methods.rb +2 -0
- data/lib/quality_extensions/object/non.rb +12 -0
- data/lib/quality_extensions/pathname.rb +435 -7
- data/lib/quality_extensions/range_list.rb +222 -0
- data/lib/quality_extensions/safe_nil.rb +5 -3
- data/lib/quality_extensions/string/each_char_with_index.rb +1 -2
- data/lib/quality_extensions/string/integer_eh.rb +3 -0
- data/lib/quality_extensions/string/numeric_eh.rb +74 -0
- data/lib/quality_extensions/string/safe_in_comment.rb +38 -0
- data/lib/quality_extensions/string/safe_numeric_conversion.rb +102 -0
- data/lib/quality_extensions/string/shell_escape.rb +4 -4
- data/lib/quality_extensions/string/with_knowledge_of_color.rb +8 -0
- data/lib/quality_extensions/table.rb +116 -0
- data/lib/quality_extensions/template.rb +4 -5
- data/lib/quality_extensions/template.rb_test_unit.rb +33 -0
- data/lib/quality_extensions/test/difference_highlighting-minitest.rb +321 -0
- data/lib/quality_extensions/test/difference_highlighting-test_unit.rb +325 -0
- data/lib/quality_extensions/test/difference_highlighting.rb +6 -314
- data/lib/quality_extensions/timeout/countdown_timer.rb +1 -0
- data/lib/quality_extensions/vector/enumerable.rb +51 -0
- metadata +35 -5
@@ -0,0 +1,110 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick, Erik Veenstra
|
3
|
+
# Copyright:: -
|
4
|
+
# License:: -
|
5
|
+
# Submit to Facets?:: Maybe
|
6
|
+
# Developer notes::
|
7
|
+
# Changes::
|
8
|
+
# * Created, based on Facets group_by
|
9
|
+
#++
|
10
|
+
|
11
|
+
module Enumerable
|
12
|
+
|
13
|
+
# #group_by_and_map is used to group items in a collection by something they
|
14
|
+
# have in common. The common factor is the key in the resulting hash, the
|
15
|
+
# array of like elements is the value.
|
16
|
+
#
|
17
|
+
# This differs from the normal group_by in that it lets you map the values
|
18
|
+
# (perhaps removing the key from the value since that would be redundant)
|
19
|
+
# all in one step.
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# # need better example
|
23
|
+
# (1..6).group_by_and_map { |n| next n % 3, n }
|
24
|
+
# # => { 0 => [3,6], 1 => [1, 4], 2 => [2,5] }
|
25
|
+
#
|
26
|
+
# [
|
27
|
+
# ['31a4', 'v1.3'],
|
28
|
+
# ['9f2b', 'current'],
|
29
|
+
# ['9f2b', 'v2.0']
|
30
|
+
# ].group_by_and_map { |e| e[0], e[1] }
|
31
|
+
# # => {"31a4"=>["v1.3"], "9f2b"=>["current", "v2.0"]}
|
32
|
+
#
|
33
|
+
# results.group_by_and_map { |a| a[0], a[1..-1] }
|
34
|
+
#
|
35
|
+
# CREDIT: Erik Veenstra, Tyler Rick
|
36
|
+
|
37
|
+
def group_by_and_map #:yield:
|
38
|
+
h = Hash.new
|
39
|
+
each { |e|
|
40
|
+
result = yield(e)
|
41
|
+
#p result
|
42
|
+
(h[result[0]] ||= []) << result[1]
|
43
|
+
}
|
44
|
+
h
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# _____ _
|
50
|
+
# |_ _|__ ___| |_
|
51
|
+
# | |/ _ \/ __| __|
|
52
|
+
# | | __/\__ \ |_
|
53
|
+
# |_|\___||___/\__|
|
54
|
+
#
|
55
|
+
=begin test
|
56
|
+
require 'spec'
|
57
|
+
|
58
|
+
describe 'Enumerable#group_by_and_map' do
|
59
|
+
it '' do
|
60
|
+
(1..6).group_by_and_map { |n| next n % 3, n }.should ==
|
61
|
+
{0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
|
62
|
+
end
|
63
|
+
|
64
|
+
it '' do
|
65
|
+
[
|
66
|
+
['r1', 'v1.3'],
|
67
|
+
['r9', 'current'],
|
68
|
+
['r9', 'v2.0']
|
69
|
+
].group_by_and_map { |e| next e[0], e[1] }.should ==
|
70
|
+
{"r1"=>["v1.3"], "r9"=>["current", "v2.0"]}
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'beats the alternatives' do
|
74
|
+
expected = {1=>["a", "b"], 2=>["c"], 3=>["d"]}
|
75
|
+
result = [
|
76
|
+
[1, 'a'],
|
77
|
+
[1, 'b'],
|
78
|
+
[2, 'c'],
|
79
|
+
[3, 'd'],
|
80
|
+
].group_by_and_map { |e| next e[0], e[1] }
|
81
|
+
result.should == expected
|
82
|
+
result.should be_instance_of Hash
|
83
|
+
result[1].should == ['a', 'b']
|
84
|
+
|
85
|
+
# group_by and then map? Then it's no longer a hash and is icky to work with.
|
86
|
+
result = [
|
87
|
+
[1, 'a'],
|
88
|
+
[1, 'b'],
|
89
|
+
[2, 'c'],
|
90
|
+
[3, 'd'],
|
91
|
+
].group_by { |e| e[0] }.map { |(k,v)| [k,v.map(&:last)]}
|
92
|
+
result.should == expected.to_a
|
93
|
+
result.should be_instance_of Array
|
94
|
+
# Yuck
|
95
|
+
result[0].should == [1, ['a', 'b']]
|
96
|
+
result[1].should == [2, ['c']]
|
97
|
+
|
98
|
+
# map and then group_by? As soon as you use map to discard the key so it's not in the value, that information is lost and can't be used by the group_by.
|
99
|
+
result = [
|
100
|
+
[1, 'a'],
|
101
|
+
[1, 'b'],
|
102
|
+
[2, 'c'],
|
103
|
+
[3, 'd'],
|
104
|
+
].map {|a| a[1]}
|
105
|
+
result.should == ["a", "b", "c", "d"] # oops, now how do we do a group_by on the key we just removed?
|
106
|
+
result.should be_instance_of Array
|
107
|
+
end
|
108
|
+
end
|
109
|
+
=end
|
110
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2009, Tyler Rick
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes
|
6
|
+
# Developer notes::
|
7
|
+
# History::
|
8
|
+
#++
|
9
|
+
|
10
|
+
module Enumerable
|
11
|
+
# def reject!
|
12
|
+
# if self.is_a? Range
|
13
|
+
# to_a.reject! { yield }
|
14
|
+
# else
|
15
|
+
# raise NoMethodError
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
|
19
|
+
def select!
|
20
|
+
reject! { |x| !yield(x) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# _____ _
|
26
|
+
# |_ _|__ ___| |_
|
27
|
+
# | |/ _ \/ __| __|
|
28
|
+
# | | __/\__ \ |_
|
29
|
+
# |_|\___||___/\__|
|
30
|
+
#
|
31
|
+
=begin test
|
32
|
+
require 'spec'
|
33
|
+
|
34
|
+
describe 'Enumerable#select!' do
|
35
|
+
it 'reject!' do
|
36
|
+
a = %w[a b c d]
|
37
|
+
a.reject! {|e| e =~ /[ab]/}.should == a
|
38
|
+
a.should == %w[c d]
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'select!' do
|
42
|
+
a = %w[a b c d]
|
43
|
+
a.select! {|e| e =~ /[ab]/}.should == a
|
44
|
+
a.should == %w[a b]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
=end
|
48
|
+
|
49
|
+
|
@@ -5,48 +5,200 @@
|
|
5
5
|
# Submit to Facets?:: Yes.
|
6
6
|
# Developer notes::
|
7
7
|
# Changes::
|
8
|
+
# To do::
|
9
|
+
# * !! Why does Hash#each_with_index yield |(k,v), i| but my select_with_index yields flat |k, v, i| ?
|
8
10
|
#++
|
9
11
|
|
12
|
+
require 'facets/kernel/require_local'
|
13
|
+
require_local 'select_with_index'
|
10
14
|
|
11
15
|
module Enumerable
|
16
|
+
# Original version before I changed it to use select so that Hash#select_until would return a hash instead of an array.
|
17
|
+
# def select_until(inclusive = true)
|
18
|
+
# return self unless block_given?
|
19
|
+
#
|
20
|
+
# selected = []
|
21
|
+
# if inclusive
|
22
|
+
# each do |item|
|
23
|
+
# selected << item
|
24
|
+
# break if yield(item)
|
25
|
+
# end
|
26
|
+
# else
|
27
|
+
# each do |item|
|
28
|
+
# break if yield(item)
|
29
|
+
# selected << item
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# selected
|
33
|
+
# end
|
34
|
+
|
12
35
|
# Returns an array containing all _consecutive_ elements of +enum+ for which +block+ is not false, starting at the first element.
|
13
|
-
# So it is very much like select
|
36
|
+
# So it is very much like +select+, but unlike +select+, it stops searching as soon as +block+ ceases to be true. (That means it will stop searching immediately if the first element doesn't match.)
|
14
37
|
#
|
15
38
|
# This might be preferable to +select+, for example, if:
|
16
39
|
# * you have a very large collection of elements
|
17
40
|
# * the desired elements are expected to all be consecutively occuring and are all at the beginning of the collection
|
18
41
|
# * it would be costly to continue iterating all the way to the very end
|
19
42
|
#
|
20
|
-
#
|
43
|
+
# If +inclusive+ is false, only those elements occuring _before_ the first element for which +block+ is true will be returned.
|
44
|
+
# If +inclusive+ is true (the default), those elements occuring up to and _including_ the first element for which +block+ is true will be returned.
|
45
|
+
#
|
46
|
+
# (0..3).select_until {|v| v == 1} # => [0, 1]
|
47
|
+
# (0..3).select_until(false) {|v| v == 1} # => [0]
|
48
|
+
#
|
49
|
+
# {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2} ) # => {"a"=>1, "b"=>2}
|
50
|
+
# {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2} ) # => {"a"=>1}
|
51
|
+
#
|
52
|
+
def select_until(inclusive = true)
|
53
|
+
return self unless block_given?
|
54
|
+
|
55
|
+
done = false
|
56
|
+
if inclusive
|
57
|
+
select do |*args|
|
58
|
+
returning = !done
|
59
|
+
done = true if yield(*args)
|
60
|
+
returning
|
61
|
+
end
|
62
|
+
else
|
63
|
+
select do |*args|
|
64
|
+
done = true if yield(*args)
|
65
|
+
!done
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Same as select_until but each time an element of the enum is yielded, an index/counter is also included as an argument.
|
21
71
|
#
|
22
|
-
#
|
72
|
+
# This is probably only useful for collections that have some kind of predictable ordering (such as Arrays). Fortunately, Hashes have a predictable order in Ruby 1.9.
|
23
73
|
#
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
74
|
+
def select_until_with_index(inclusive = true)
|
75
|
+
return self unless block_given?
|
76
|
+
|
77
|
+
done = false
|
78
|
+
if inclusive
|
79
|
+
select_with_index do |*args|
|
80
|
+
returning = !done
|
81
|
+
done = true if yield(*args)
|
82
|
+
returning
|
30
83
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
84
|
+
else
|
85
|
+
select_with_index do |*args|
|
86
|
+
done = true if yield(*args)
|
87
|
+
!done
|
35
88
|
end
|
36
|
-
|
37
|
-
selected
|
89
|
+
end
|
38
90
|
end
|
39
91
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
92
|
+
# Better name? select_consec?
|
93
|
+
def select_while(include_first_false_element = false)
|
94
|
+
return self unless block_given?
|
95
|
+
|
96
|
+
done = false
|
97
|
+
if include_first_false_element
|
98
|
+
select do |*args|
|
99
|
+
returning = !done
|
100
|
+
done = true if !yield(*args)
|
101
|
+
returning
|
102
|
+
end
|
103
|
+
else
|
104
|
+
select do |*args|
|
105
|
+
#puts "!done=#{!done}; !yield(#{args}) => #{!yield(*args)}"
|
106
|
+
done = true if !yield(*args)
|
107
|
+
!done
|
108
|
+
end
|
45
109
|
end
|
46
|
-
selected
|
47
110
|
end
|
48
|
-
end
|
49
111
|
|
112
|
+
def select_while_with_index(include_first_false_element = false)
|
113
|
+
return self unless block_given?
|
114
|
+
|
115
|
+
done = false
|
116
|
+
if include_first_false_element
|
117
|
+
select_with_index do |*args|
|
118
|
+
returning = !done
|
119
|
+
done = true if !yield(*args)
|
120
|
+
returning
|
121
|
+
end
|
122
|
+
else
|
123
|
+
select_with_index do |*args|
|
124
|
+
#puts "!done=#{!done}; !yield(#{args}) => #{!yield(*args)}"
|
125
|
+
done = true if !yield(*args)
|
126
|
+
!done
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Whereas select_until goes until it reaches the _first_ element for which +block+ is true, select_until_last goes until it reaches the _last_ element for which +block+ is true.
|
132
|
+
#
|
133
|
+
def select_until_last(inclusive = true)
|
134
|
+
return self unless block_given?
|
135
|
+
|
136
|
+
# Find the index of the last-matching element
|
137
|
+
last = nil
|
138
|
+
#each_with_index do |item, i|
|
139
|
+
# last = i if yield(item)
|
140
|
+
#end
|
141
|
+
each_with_index do |*args|
|
142
|
+
i = args.pop
|
143
|
+
last = i if yield(*args.first)
|
144
|
+
end
|
145
|
+
|
146
|
+
# If they want to exclude the last-matching element, adjust the index by -1 (if possible)
|
147
|
+
#if last && !inclusive
|
148
|
+
# if last == 0
|
149
|
+
# # If the last-matching element was also the first element, then we want to select *none* of the elements
|
150
|
+
# last = nil
|
151
|
+
# else
|
152
|
+
# last -= 1
|
153
|
+
# end
|
154
|
+
#end
|
155
|
+
# Select all elements up to last-matching element
|
156
|
+
#self.to_a[0 .. last] # (didn't work for hashes)
|
157
|
+
#select_with_index do |item, i|
|
158
|
+
# true if last && i <= last
|
159
|
+
#end
|
160
|
+
#select_while_with_index do |item, i|
|
161
|
+
# #puts "#{i} <= #{last} => #{last && i <= last}"
|
162
|
+
# last && i <= last
|
163
|
+
#end
|
164
|
+
|
165
|
+
# Select all elements up to last-matching element
|
166
|
+
if last.nil?
|
167
|
+
select do |*args|
|
168
|
+
false
|
169
|
+
end
|
170
|
+
else
|
171
|
+
select_until_with_index(inclusive) do |*args|
|
172
|
+
i = args.last
|
173
|
+
i == last
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def select_until_last_with_index(inclusive = true)
|
179
|
+
return self unless block_given?
|
180
|
+
|
181
|
+
# Find the index of the last-matching element
|
182
|
+
last = nil
|
183
|
+
each_with_index do |*args|
|
184
|
+
#p args
|
185
|
+
i = args.last
|
186
|
+
last = i if yield(*args)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Select all elements up to last-matching element
|
190
|
+
if last.nil?
|
191
|
+
select do |*args|
|
192
|
+
false
|
193
|
+
end
|
194
|
+
else
|
195
|
+
select_until_with_index(inclusive) do |*args|
|
196
|
+
i = args.last
|
197
|
+
i == last
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
50
202
|
|
51
203
|
|
52
204
|
|
@@ -59,50 +211,183 @@ end
|
|
59
211
|
#
|
60
212
|
=begin test
|
61
213
|
require 'test/unit'
|
214
|
+
require 'facets/string/indent'
|
215
|
+
|
216
|
+
class SelectWhileAndUntilTest < Test::Unit::TestCase
|
217
|
+
def test_basic_usage
|
218
|
+
assert_equal [0, 1], (0..3).select {|v| v <= 1}
|
219
|
+
assert_equal [0, 1], (0..3).select_while {|v| v <= 1}
|
220
|
+
assert_equal [0, 1], (0..3).select_until {|v| v == 1}
|
221
|
+
assert_equal [0], (0..3).select_until(false) {|v| v == 1}
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_basic_usage_with_index
|
225
|
+
assert_equal %w[a b], %w[a b c].select_with_index {|v, i| i <= 1}
|
226
|
+
assert_equal %w[a b], %w[a b c].select_while_with_index {|v, i| i <= 1}
|
227
|
+
assert_equal %w[a b], %w[a b c].select_until_with_index {|v, i| i == 1}
|
228
|
+
assert_equal %w[a], %w[a b c].select_until_with_index(false) {|v, i| i == 1}
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_when_no_matches_found
|
232
|
+
assert_equal [], (0..3).select {|v| false}
|
233
|
+
assert_equal [], (0..3).select_while {|v| false}
|
234
|
+
assert_equal [0], (0..3).select_until {|v| true}
|
235
|
+
assert_equal [], (0..3).select_until(false) {|v| true}
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_with_hashes_basic_usage
|
239
|
+
if RUBY_VERSION < '1.9'
|
240
|
+
# Yuck! Hash#select returns an array instead of returning a hash like reject.
|
241
|
+
assert_equal [["a", 1], ["b", 2], ["d", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| v <= 2}
|
242
|
+
assert_equal [["a", 1], ["b", 2] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| v <= 2}
|
243
|
+
assert_equal [["a", 1], ["b", 2] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2}
|
244
|
+
assert_equal [["a", 1] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2}
|
245
|
+
else
|
246
|
+
assert_equal( {"a"=>1, "b"=>2, "d"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| v <= 2})
|
247
|
+
assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| v <= 2} )
|
248
|
+
assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2} )
|
249
|
+
assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2} )
|
250
|
+
end
|
251
|
+
end
|
62
252
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
253
|
+
def test_with_hashes_basic_usage_with_index
|
254
|
+
if RUBY_VERSION < '1.9'
|
255
|
+
assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_with_index {|k, v, i| i <= 1}
|
256
|
+
assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while_with_index {|k, v, i| i <= 1}
|
257
|
+
assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index {|k, v, i| i == 1}
|
258
|
+
assert_equal [["a", 1] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index(false) {|k, v, i| i == 1}
|
259
|
+
else
|
260
|
+
assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_with_index {|k, v, i| i <= 1} )
|
261
|
+
assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while_with_index {|k, v, i| i <= 1} )
|
262
|
+
assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index {|k, v, i| i == 1} )
|
263
|
+
assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index(false) {|k, v, i| i == 1} )
|
264
|
+
end
|
67
265
|
end
|
68
266
|
|
69
|
-
def
|
267
|
+
def test_with_hashes_when_no_matches_found
|
268
|
+
if RUBY_VERSION < '1.9'
|
269
|
+
assert_equal [], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| false}
|
270
|
+
assert_equal [], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| false}
|
271
|
+
assert_equal [["a", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| true}
|
272
|
+
assert_equal [], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| true}
|
273
|
+
else
|
274
|
+
assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| false})
|
275
|
+
assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| false} )
|
276
|
+
assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| true} )
|
277
|
+
assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| true} )
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
def test_show_how_it_differs_from_plain_old_select
|
70
283
|
# Ah, yes, it behaves the same as select in *this* simple case:
|
71
|
-
assert_equal [1, 2], (1..4).select {|
|
284
|
+
assert_equal [1, 2], (1..4).select {|v| v <= 2}
|
72
285
|
|
73
286
|
# But what about _this_ one... hmm?
|
74
|
-
assert_equal [1, 2], [1, 2, 3, 2, 1].select_while {|
|
75
|
-
assert_equal [1, 2, 2, 1], [1, 2, 3, 2, 1].select
|
287
|
+
assert_equal [1, 2], [1, 2, 3, 2, 1].select_while {|v| v <= 2}
|
288
|
+
assert_equal [1, 2, 2, 1], [1, 2, 3, 2, 1].select {|v| v <= 2} # Not the same! Keyword: _consecutive_.
|
76
289
|
|
77
290
|
# Or _this_ one...
|
78
|
-
assert_equal [1, 2, 1], [1, 2, 1, 99, 2].select_while {|
|
79
|
-
assert_equal [1, 2], [1, 2, 1, 99, 2].select {|
|
291
|
+
assert_equal [1, 2, 1], [1, 2, 1, 99, 2].select_while {|v| v <= 2}
|
292
|
+
assert_equal [1, 2], [1, 2, 1, 99, 2].select {|v| v <= 2}.uniq # Even this isn't the same.
|
80
293
|
end
|
81
294
|
|
82
|
-
def
|
295
|
+
def test_example_use_of_inclusive_option
|
296
|
+
# Let's say we have an array with these lines of code:
|
297
|
+
lines_of_code = <<-End.indent(-6).lines.map(&:chomp)
|
298
|
+
def a
|
299
|
+
:a
|
300
|
+
end
|
301
|
+
def b
|
302
|
+
:b
|
303
|
+
end
|
304
|
+
End
|
305
|
+
#puts lines_of_code.to_a
|
306
|
+
# ... and we want to match up to and including the first line that matches /end/.
|
307
|
+
|
308
|
+
assert_equal [
|
309
|
+
'def a',
|
310
|
+
' :a',
|
311
|
+
],
|
312
|
+
lines_of_code.select_until(false) {|line| line =~ /end/}
|
313
|
+
# It didn't go far enough. We actually want to *include* that last element. This is when we'd want to use inclusive=true.
|
314
|
+
|
83
315
|
assert_equal [
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
'end',
|
90
|
-
].select_until {|line| line =~ /end/}
|
91
|
-
# Not far enough. We actually want to *include* that last element.
|
316
|
+
'def a',
|
317
|
+
' :a',
|
318
|
+
'end',
|
319
|
+
],
|
320
|
+
lines_of_code.select_until(true) {|line| line =~ /end/}
|
92
321
|
|
93
322
|
assert_equal [
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
323
|
+
'def a',
|
324
|
+
' :a',
|
325
|
+
],
|
326
|
+
lines_of_code.select_while(false) {|line| line !~ /end/}
|
327
|
+
|
328
|
+
# Although a bit contrived, this is an example of when you might want to use include_first_false_element = true
|
329
|
+
# In practice, you'd probably seldom have a use for it, but I thought I should include it just to be consistent with select_until. Disagree?
|
330
|
+
assert_equal [
|
331
|
+
'def a',
|
332
|
+
' :a',
|
333
|
+
'end',
|
334
|
+
],
|
335
|
+
lines_of_code.select_while(true) {|line| line !~ /end/}
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
class SelectUntilLastTest < Test::Unit::TestCase
|
340
|
+
def test_simplest_case
|
341
|
+
assert_equal [1], [1].select_until_last {|v| v == 1}
|
342
|
+
assert_equal [], [1].select_until_last(false) {|v| v == 1} # if exclusive, we won't even return the one and only element
|
343
|
+
end
|
344
|
+
|
345
|
+
def test_basic_usage
|
346
|
+
assert_equal [1, 2, 1], [1, 2, 1, 2].select_until_last {|v| v == 1}
|
347
|
+
assert_equal [1, 2], [1, 2, 1, 2].select_until_last(false) {|v| v == 1}
|
348
|
+
end
|
349
|
+
|
350
|
+
def test_basic_usage_with_index
|
351
|
+
assert_equal %w[a b], %w[a b c].select_until_last_with_index {|v, i| i == 1}
|
352
|
+
assert_equal %w[a], %w[a b c].select_until_last_with_index(false) {|v, i| i == 1}
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_when_no_matches_found
|
356
|
+
assert_equal [], [1, 2, 1, 2].select_until_last {|v| v == 3}
|
357
|
+
assert_equal [], [1, 2, 1, 2].select_until_last(false) {|v| v == 3}
|
358
|
+
assert_equal [], [1, 2, 1, 2].select_until_last_with_index(false) {|v, i| v == 3}
|
359
|
+
# It should be the same as doing this:
|
360
|
+
assert_equal [], [1, 2, 1, 2].select {|v| false}
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_with_hashes_basic_usage
|
364
|
+
if RUBY_VERSION < '1.9'
|
365
|
+
assert_equal [["a", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 1}
|
366
|
+
assert_equal [ ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 1}
|
367
|
+
|
368
|
+
# See the difference:
|
369
|
+
assert_equal [["a", 1], ["b", 2], ["c", 3], ["d", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last {|k, v| v == 1}
|
370
|
+
assert_equal [["a", 1], ["b", 2], ["c", 3] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last(false) {|k, v| v == 1}
|
371
|
+
else
|
372
|
+
assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 1} )
|
373
|
+
assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 1} )
|
374
|
+
|
375
|
+
# See the difference:
|
376
|
+
assert_equal( {"a"=>1, "b"=>2, "c"=>3, "d"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last {|k, v| v == 1} )
|
377
|
+
assert_equal( {"a"=>1, "b"=>2, "c"=>3 }, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last(false) {|k, v| v == 1} )
|
378
|
+
end
|
105
379
|
end
|
380
|
+
|
381
|
+
def test_with_hashes_basic_usage_with_index
|
382
|
+
if RUBY_VERSION < '1.9'
|
383
|
+
assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index {|(k, v), i| p=[k,v,i]; i <= 1}
|
384
|
+
assert_equal [["a", 1] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index(false) {|(k, v), i| i == 1}
|
385
|
+
else
|
386
|
+
assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index {|(k, v), i| p=[k,v,i]; i <= 1} )
|
387
|
+
assert_equal( {"a"=>1 }, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index(false) {|(k, v), i| i == 1} )
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
106
391
|
end
|
107
392
|
=end
|
108
393
|
|