linked-list 0.0.10 → 0.0.15
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/.travis.yml +3 -2
- data/CHANGELOG.md +59 -0
- data/README.md +25 -8
- data/lib/linked-list/conversions.rb +3 -5
- data/lib/linked-list/list.rb +156 -1
- data/lib/linked-list/version.rb +1 -1
- data/linked-list.gemspec +6 -5
- data/test/list_test.rb +538 -0
- data/test/test_helper.rb +4 -2
- metadata +38 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a70c658176bc2e924764731304a3f8dbb376306e35b9e016d41896a7b5790463
|
4
|
+
data.tar.gz: cde342c544b41d53f5c52eb3d592deed28a6bf79736b6a0f2dbb1ef2d78319a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db29d3d0ec5195c12937ae63332a8dbb3ac891f6510441762f5a42d1acfce8de826d9a20a6af308e9b39a5fe33fcf92527c26d1ffd757c6814fb2c09ed4c34a4
|
7
|
+
data.tar.gz: 7723a09dc0858687eaf4ff6668e024ba8b7efd00cc202ab6eb067baffc52f645f6138d286cec946ccb30896b372345f91b601db5e8dee8900c65582ffe61577b
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# 0.0.x / Unreleased
|
2
|
+
|
3
|
+
|
4
|
+
# 0.0.15 / 2020-05-26
|
5
|
+
|
6
|
+
## Fixed
|
7
|
+
|
8
|
+
- Fixed bug when deleting the last node of the list. The @tail value was not updated if the @head value was updated.
|
9
|
+
|
10
|
+
[Compare v0.0.14...v0.0.15](https://github.com/spectator/linked-list/compare/v0.0.14...v0.0.15)
|
11
|
+
|
12
|
+
# 0.0.14 / 2020-04-17
|
13
|
+
|
14
|
+
## Fixed
|
15
|
+
|
16
|
+
Forced minimal rake version in gemspec to skip rake versions with security issues.
|
17
|
+
|
18
|
+
[Compare v0.0.13...v0.0.14](https://github.com/spectator/linked-list/compare/v0.0.13...v0.0.14)
|
19
|
+
|
20
|
+
# 0.0.13 / 2018-11-27
|
21
|
+
|
22
|
+
## Fixed
|
23
|
+
|
24
|
+
- Delete duplicate method definitions (nex3 in [#7](https://github.com/spectator/linked-list/pull/7))
|
25
|
+
|
26
|
+
## Added
|
27
|
+
|
28
|
+
- Allow `List#delete` to delete a `Node` in O(1) time (nex3 in [#6](https://github.com/spectator/linked-list/pull/6))
|
29
|
+
|
30
|
+
[Compare v0.0.12...v0.0.13](https://github.com/spectator/linked-list/compare/v0.0.12...v0.0.13)
|
31
|
+
|
32
|
+
# 0.0.12 / 2018-09-04
|
33
|
+
|
34
|
+
## Added
|
35
|
+
|
36
|
+
- Added `insert`, `insert_before` and `insert_after` methods (mpospelov in [#3](https://github.com/spectator/linked-list/pull/3))
|
37
|
+
- Added `reverse_each` and `reverse_each_node` methods (mpospelov in [#4](https://github.com/spectator/linked-list/pull/4))
|
38
|
+
|
39
|
+
[Compare v0.0.11...v0.0.12](https://github.com/spectator/linked-list/compare/v0.0.11...v0.0.12)
|
40
|
+
|
41
|
+
# 0.0.11 / 2018-08-23
|
42
|
+
|
43
|
+
## Added
|
44
|
+
|
45
|
+
- Added `delete` and `delete_all` methods (mpospelov in [#2](https://github.com/spectator/linked-list/pull/2))
|
46
|
+
|
47
|
+
[Compare v0.0.10...v0.0.11](https://github.com/spectator/linked-list/compare/v0.0.10...v0.0.11)
|
48
|
+
|
49
|
+
# 0.0.10 / 2018-04-02
|
50
|
+
|
51
|
+
## Fixed
|
52
|
+
|
53
|
+
- Fixed bug when `@tail.prev` was mistekenly set to `nil` instead of `@tail.next` when popping elements off the list (Sonna in [#1](https://github.com/spectator/linked-list/pull/1))
|
54
|
+
|
55
|
+
[Compare v0.0.9...v0.0.10](https://github.com/spectator/linked-list/compare/v0.0.9...v0.0.10)
|
56
|
+
|
57
|
+
# 0.0.9 / 2013-12-11
|
58
|
+
|
59
|
+
Initial release.
|
data/README.md
CHANGED
@@ -41,17 +41,38 @@ list.unshift(object)
|
|
41
41
|
list.pop
|
42
42
|
list.shift
|
43
43
|
|
44
|
+
list.insert(object, before: object)
|
45
|
+
list.insert(object, before: ->(n) { n == 'foo' })
|
46
|
+
|
47
|
+
list.insert(object, after: object)
|
48
|
+
list.insert(object, after: ->(n) { n == 'foo' })
|
49
|
+
|
50
|
+
list.insert_before(object, node)
|
51
|
+
list.insert_after(object, node)
|
52
|
+
|
44
53
|
list.reverse
|
45
54
|
list.reverse!
|
46
55
|
|
47
|
-
list.
|
56
|
+
list.delete(object)
|
57
|
+
list.delete { |n| n == 'foo' }
|
58
|
+
|
59
|
+
list.delete_all(object)
|
60
|
+
list.delete_all { |n| n == 'foo' }
|
61
|
+
|
62
|
+
list.each # Enumerator object
|
48
63
|
list.each { |e| puts e }
|
49
64
|
|
50
|
-
list.
|
51
|
-
list.
|
65
|
+
list.reverse_each # Enumerator object
|
66
|
+
list.reverse_each { |e| puts e }
|
67
|
+
|
68
|
+
list.reverse_each_node # Enumerator object
|
69
|
+
list.reverse_each_node { |node| puts node.data }
|
70
|
+
|
71
|
+
list.first # head of the list
|
72
|
+
list.last # tail of the list
|
52
73
|
|
53
74
|
list.length
|
54
|
-
list.size
|
75
|
+
list.size # same as `length`
|
55
76
|
|
56
77
|
list.to_a
|
57
78
|
```
|
@@ -76,10 +97,6 @@ List([object, object]) # will return new `List` object with two `Node` objects
|
|
76
97
|
Please see `LinkedList::List`, `LinkedList::Node`, and
|
77
98
|
`LinkedList::Conversions` for details.
|
78
99
|
|
79
|
-
## TODO
|
80
|
-
|
81
|
-
* Insert / delete in the middle
|
82
|
-
|
83
100
|
## Tests
|
84
101
|
|
85
102
|
Run test with
|
@@ -12,8 +12,7 @@ module LinkedList
|
|
12
12
|
# New +Node+ object.
|
13
13
|
#
|
14
14
|
def Node(arg)
|
15
|
-
|
16
|
-
when ->(_arg) { _arg.respond_to?(:to_node) }
|
15
|
+
if arg.respond_to?(:to_node)
|
17
16
|
arg.to_node
|
18
17
|
else
|
19
18
|
Node.new(arg)
|
@@ -31,10 +30,9 @@ module LinkedList
|
|
31
30
|
# New +List+ object.
|
32
31
|
#
|
33
32
|
def List(arg)
|
34
|
-
|
35
|
-
when ->(_arg) { _arg.respond_to?(:to_list) }
|
33
|
+
if arg.respond_to?(:to_list)
|
36
34
|
arg.to_list
|
37
|
-
|
35
|
+
elsif arg.respond_to?(:to_ary)
|
38
36
|
arg.to_ary.each_with_object(List.new) { |n, l| l.push(Node(n)) }
|
39
37
|
else
|
40
38
|
List.new.push(Node(arg))
|
data/lib/linked-list/list.rb
CHANGED
@@ -4,7 +4,7 @@ module LinkedList
|
|
4
4
|
class List
|
5
5
|
include Conversions
|
6
6
|
|
7
|
-
attr_reader
|
7
|
+
attr_reader :length
|
8
8
|
alias_method :size, :length
|
9
9
|
|
10
10
|
def initialize
|
@@ -69,6 +69,98 @@ module LinkedList
|
|
69
69
|
self
|
70
70
|
end
|
71
71
|
|
72
|
+
# Inserts after or before first matched node.data from the the list by passed block or value.
|
73
|
+
#
|
74
|
+
# == Returns:
|
75
|
+
# Inserted node data
|
76
|
+
#
|
77
|
+
def insert(to_add, after: nil, before: nil)
|
78
|
+
if after && before || !after && !before
|
79
|
+
raise ArgumentError, 'either :after or :before keys should be passed'
|
80
|
+
end
|
81
|
+
matcher = after || before
|
82
|
+
matcher_proc = if matcher.is_a?(Proc)
|
83
|
+
__to_matcher(&matcher)
|
84
|
+
else
|
85
|
+
__to_matcher(matcher)
|
86
|
+
end
|
87
|
+
node = each_node.find(&matcher_proc)
|
88
|
+
return unless node
|
89
|
+
new_node = after ? insert_after_node(to_add, node) : insert_before_node(to_add, node)
|
90
|
+
new_node.data
|
91
|
+
end
|
92
|
+
|
93
|
+
# Inserts data after first matched node.data.
|
94
|
+
#
|
95
|
+
# == Returns:
|
96
|
+
# Inserted node
|
97
|
+
#
|
98
|
+
def insert_after_node(data, node)
|
99
|
+
Node(data).tap do |new_node|
|
100
|
+
new_node.prev = node
|
101
|
+
new_node.next = node.next
|
102
|
+
if node.next
|
103
|
+
node.next.prev = new_node
|
104
|
+
else
|
105
|
+
@tail = new_node
|
106
|
+
end
|
107
|
+
node.next = new_node
|
108
|
+
@length += 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Inserts data before first matched node.data.
|
113
|
+
#
|
114
|
+
# == Returns:
|
115
|
+
# Inserted node
|
116
|
+
#
|
117
|
+
def insert_before_node(data, node)
|
118
|
+
Node(data).tap do |new_node|
|
119
|
+
new_node.next = node
|
120
|
+
new_node.prev = node.prev
|
121
|
+
if node.prev
|
122
|
+
node.prev.next = new_node
|
123
|
+
else
|
124
|
+
@head = new_node
|
125
|
+
end
|
126
|
+
node.prev = new_node
|
127
|
+
@length += 1
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Removes first matched node.data from the the list by passed block or value.
|
132
|
+
#
|
133
|
+
# If +val+ is a +Node+, removes that node from the list. Behavior is
|
134
|
+
# undefined if +val+ is a +Node+ that's not a member of this list.
|
135
|
+
#
|
136
|
+
# == Returns:
|
137
|
+
# Deleted node's data
|
138
|
+
#
|
139
|
+
def delete(val = nil, &block)
|
140
|
+
if val.respond_to?(:to_node)
|
141
|
+
node = val.to_node
|
142
|
+
__unlink_node(node)
|
143
|
+
return node.data
|
144
|
+
end
|
145
|
+
|
146
|
+
each_node.find(&__to_matcher(val, &block)).tap do |node_to_delete|
|
147
|
+
return unless node_to_delete
|
148
|
+
__unlink_node(node_to_delete)
|
149
|
+
end.data
|
150
|
+
end
|
151
|
+
|
152
|
+
# Removes all matched data.data from the the list by passed block or value.
|
153
|
+
#
|
154
|
+
# == Returns:
|
155
|
+
# Array of deleted nodes
|
156
|
+
#
|
157
|
+
def delete_all(val = nil, &block)
|
158
|
+
each_node.select(&__to_matcher(val, &block)).each do |node_to_delete|
|
159
|
+
next unless node_to_delete
|
160
|
+
__unlink_node(node_to_delete)
|
161
|
+
end.map(&:data)
|
162
|
+
end
|
163
|
+
|
72
164
|
# Removes data from the end of the list.
|
73
165
|
#
|
74
166
|
# == Returns:
|
@@ -136,6 +228,41 @@ module LinkedList
|
|
136
228
|
__each { |node| yield(node.data) }
|
137
229
|
end
|
138
230
|
|
231
|
+
# Iterates over nodes from top to bottom passing node(LinkedList::Node instance)
|
232
|
+
# to the block if given. If no block given, returns +Enumerator+.
|
233
|
+
#
|
234
|
+
# == Returns:
|
235
|
+
# +Enumerator+ or yields list nodes to the block
|
236
|
+
#
|
237
|
+
def each_node
|
238
|
+
return to_enum(__callee__) unless block_given?
|
239
|
+
__each { |node| yield(node) }
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
# Iterates over nodes from bottom to top passing node data to the block if
|
244
|
+
# given. If no block given, returns +Enumerator+.
|
245
|
+
#
|
246
|
+
# == Returns:
|
247
|
+
# +Enumerator+ or yields data to the block stored in every node on the
|
248
|
+
# list.
|
249
|
+
#
|
250
|
+
def reverse_each
|
251
|
+
return to_enum(__callee__) unless block_given?
|
252
|
+
__reverse_each { |node| yield(node.data) }
|
253
|
+
end
|
254
|
+
|
255
|
+
# Iterates over nodes from bottom to top passing node(LinkedList::Node instance)
|
256
|
+
# to the block if given. If no block given, returns +Enumerator+.
|
257
|
+
#
|
258
|
+
# == Returns:
|
259
|
+
# +Enumerator+ or yields list nodes to the block
|
260
|
+
#
|
261
|
+
def reverse_each_node
|
262
|
+
return to_enum(__callee__) unless block_given?
|
263
|
+
__reverse_each { |node| yield(node) }
|
264
|
+
end
|
265
|
+
|
139
266
|
# Converts list to array.
|
140
267
|
#
|
141
268
|
def to_a
|
@@ -158,6 +285,26 @@ module LinkedList
|
|
158
285
|
|
159
286
|
private
|
160
287
|
|
288
|
+
def __unlink_node(node)
|
289
|
+
@head = node.next if node.prev.nil?
|
290
|
+
@tail = node.prev if node.next.nil?
|
291
|
+
|
292
|
+
if node.prev.nil?
|
293
|
+
node.next.prev = nil if node.next
|
294
|
+
elsif node.next.nil?
|
295
|
+
node.prev.next = nil if node.prev
|
296
|
+
else
|
297
|
+
node.prev.next, node.next.prev = node.next, node.prev
|
298
|
+
end
|
299
|
+
@length -= 1
|
300
|
+
end
|
301
|
+
|
302
|
+
def __to_matcher(val = nil, &block)
|
303
|
+
raise ArgumentError, 'either value or block should be passed' if val && block_given?
|
304
|
+
block = ->(e) { e == val } unless block_given?
|
305
|
+
->(node) { block.call(node.data) }
|
306
|
+
end
|
307
|
+
|
161
308
|
def __shift
|
162
309
|
head = @head
|
163
310
|
@head = @head.next
|
@@ -172,6 +319,14 @@ module LinkedList
|
|
172
319
|
tail
|
173
320
|
end
|
174
321
|
|
322
|
+
def __reverse_each
|
323
|
+
curr_node = @tail
|
324
|
+
while(curr_node)
|
325
|
+
yield curr_node
|
326
|
+
curr_node = curr_node.prev
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
175
330
|
def __each
|
176
331
|
curr_node = @head
|
177
332
|
while(curr_node)
|
data/lib/linked-list/version.rb
CHANGED
data/linked-list.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Linked::List::VERSION
|
9
9
|
spec.authors = ['Yury Velikanau']
|
10
10
|
spec.email = ['yury.velikanau@gmail.com']
|
11
|
-
spec.description = %q
|
12
|
-
spec.summary = %q
|
11
|
+
spec.description = %q(Ruby implementation of Doubly Linked List, following some Ruby idioms.)
|
12
|
+
spec.summary = %q(Ruby implementation of Doubly Linked List, following some Ruby idioms.)
|
13
13
|
spec.homepage = 'https://github.com/spectator/linked-list'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_development_dependency '
|
22
|
-
spec.add_development_dependency '
|
21
|
+
spec.add_development_dependency 'bundler', '>= 2.0', '< 3.0'
|
22
|
+
spec.add_development_dependency 'coveralls', '~> 0'
|
23
|
+
spec.add_development_dependency 'm', '~> 1.5', '>= 1.5.0'
|
23
24
|
spec.add_development_dependency 'minitest', '>= 5.0', '<= 6.0'
|
24
|
-
spec.add_development_dependency '
|
25
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
25
26
|
end
|
data/test/list_test.rb
CHANGED
@@ -4,6 +4,7 @@ describe LinkedList::List do
|
|
4
4
|
let(:list) { create_list }
|
5
5
|
let(:node_1) { create_node('foo') }
|
6
6
|
let(:node_2) { create_node('bar') }
|
7
|
+
let(:node_3) { create_node('baz') }
|
7
8
|
|
8
9
|
describe 'instantiation' do
|
9
10
|
it 'assigns first to nil' do
|
@@ -134,6 +135,486 @@ describe LinkedList::List do
|
|
134
135
|
end
|
135
136
|
end
|
136
137
|
|
138
|
+
describe '#insert' do
|
139
|
+
it 'raises error if after and before are passed' do
|
140
|
+
err = assert_raises ArgumentError do
|
141
|
+
assert_nil list.insert(1, after: 'x', before: 'y')
|
142
|
+
end
|
143
|
+
assert_equal err.message, 'either :after or :before keys should be passed'
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'raises error if no after nore before are passed' do
|
147
|
+
err = assert_raises ArgumentError do
|
148
|
+
assert_nil list.insert(1)
|
149
|
+
end
|
150
|
+
assert_equal err.message, 'either :after or :before keys should be passed'
|
151
|
+
end
|
152
|
+
|
153
|
+
describe 'after:' do
|
154
|
+
describe 'by block' do
|
155
|
+
it 'does not add value if insert after not found' do
|
156
|
+
list.push('foo')
|
157
|
+
assert_nil list.insert(1, after: ->(d) { d == 'foo1' })
|
158
|
+
assert_equal ['foo'], list.to_a
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'inserts value after first matching node by block' do
|
162
|
+
list.push('foo')
|
163
|
+
list.push('bar')
|
164
|
+
assert_equal 1, list.insert(1, after: ->(d) { d == 'foo' })
|
165
|
+
assert_equal ['foo', 1, 'bar'], list.to_a
|
166
|
+
assert_equal 3, list.length
|
167
|
+
end
|
168
|
+
|
169
|
+
describe 'position edge cases' do
|
170
|
+
before do
|
171
|
+
list.push(0)
|
172
|
+
list.push(1)
|
173
|
+
list.push(2)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'inserts after in the middle' do
|
177
|
+
list.insert('foo', after: ->(d) { d == 0 })
|
178
|
+
assert_equal [0, 'foo', 1, 2], list.to_a
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'inserts after the tail' do
|
182
|
+
list.insert('foo', after: ->(d) { d == 2 })
|
183
|
+
assert_equal [0, 1, 2, 'foo'], list.to_a
|
184
|
+
assert_equal 'foo', list.last
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'by value' do
|
190
|
+
it 'does not add value if insert after not found' do
|
191
|
+
list.push('foo')
|
192
|
+
assert_nil list.insert(1, after: 'foo1')
|
193
|
+
assert_equal ['foo'], list.to_a
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'inserts value after first matching node by block' do
|
197
|
+
list.push('foo')
|
198
|
+
list.push('bar')
|
199
|
+
assert_equal 1, list.insert(1, after: 'foo')
|
200
|
+
assert_equal ['foo', 1, 'bar'], list.to_a
|
201
|
+
assert_equal 3, list.length
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'position edge cases' do
|
205
|
+
before do
|
206
|
+
list.push(0)
|
207
|
+
list.push(1)
|
208
|
+
list.push(2)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'inserts after in the middle' do
|
212
|
+
list.insert('foo', after: 0)
|
213
|
+
assert_equal [0, 'foo', 1, 2], list.to_a
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'inserts after the tail' do
|
217
|
+
list.insert('foo', after: 2)
|
218
|
+
assert_equal [0, 1, 2, 'foo'], list.to_a
|
219
|
+
assert_equal 'foo', list.last
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe ':before' do
|
226
|
+
describe 'by block' do
|
227
|
+
it 'does not add value if insert before not found' do
|
228
|
+
list.push('foo')
|
229
|
+
assert_nil list.insert(1, before: ->(d) { d == 'foo1' })
|
230
|
+
assert_equal ['foo'], list.to_a
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'inserts value before first matching node by block' do
|
234
|
+
list.push('foo')
|
235
|
+
list.push('bar')
|
236
|
+
assert_equal 1, list.insert(1, before: ->(d) { d == 'foo' })
|
237
|
+
assert_equal [1, 'foo', 'bar'], list.to_a
|
238
|
+
assert_equal 3, list.length
|
239
|
+
end
|
240
|
+
|
241
|
+
describe 'position edge cases' do
|
242
|
+
before do
|
243
|
+
list.push(0)
|
244
|
+
list.push(1)
|
245
|
+
list.push(2)
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'inserts before head' do
|
249
|
+
list.insert('foo', before: ->(d) { d == 0 })
|
250
|
+
assert_equal ['foo', 0, 1, 2], list.to_a
|
251
|
+
assert_equal 'foo', list.first
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'inserts before in the middle' do
|
255
|
+
list.insert('foo', before: ->(d) { d == 2 })
|
256
|
+
assert_equal [0, 1, 'foo', 2], list.to_a
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe 'by value' do
|
262
|
+
it 'does not add value if insert before not found' do
|
263
|
+
list.push('foo')
|
264
|
+
assert_nil list.insert(1, before: 'foo1')
|
265
|
+
assert_equal ['foo'], list.to_a
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'inserts value before first' do
|
269
|
+
list.push('foo')
|
270
|
+
list.push('bar')
|
271
|
+
assert_equal 1, list.insert(1, before: 'foo')
|
272
|
+
assert_equal [1, 'foo', 'bar'], list.to_a
|
273
|
+
assert_equal 3, list.length
|
274
|
+
end
|
275
|
+
|
276
|
+
describe 'position edge cases' do
|
277
|
+
before do
|
278
|
+
list.push(0)
|
279
|
+
list.push(1)
|
280
|
+
list.push(2)
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'inserts before head' do
|
284
|
+
list.insert('foo', before: 0)
|
285
|
+
assert_equal ['foo', 0, 1, 2], list.to_a
|
286
|
+
assert_equal 'foo', list.first
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'inserts before in the middle' do
|
290
|
+
list.insert('foo', before: 2)
|
291
|
+
assert_equal [0, 1, 'foo', 2], list.to_a
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe '#insert_after_node' do
|
299
|
+
it 'inserts value after passed node' do
|
300
|
+
list.push('foo')
|
301
|
+
list.push('bar')
|
302
|
+
node = list.each_node.find { |n| n.data == 'foo' }
|
303
|
+
assert_equal 1, list.insert_after_node(1, node).data
|
304
|
+
assert_equal ['foo', 1, 'bar'], list.to_a
|
305
|
+
assert_equal 3, list.length
|
306
|
+
end
|
307
|
+
|
308
|
+
describe 'position edge cases' do
|
309
|
+
before do
|
310
|
+
list.push(0)
|
311
|
+
list.push(1)
|
312
|
+
list.push(2)
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'inserts after in the middle' do
|
316
|
+
node = list.each_node.find { |n| n.data == 0 }
|
317
|
+
list.insert_after_node('foo', node)
|
318
|
+
assert_equal [0, 'foo', 1, 2], list.to_a
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'inserts after the tail' do
|
322
|
+
node = list.each_node.find { |n| n.data == 2 }
|
323
|
+
list.insert_after_node('foo', node)
|
324
|
+
assert_equal [0, 1, 2, 'foo'], list.to_a
|
325
|
+
assert_equal 'foo', list.last
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe '#insert_after_node' do
|
331
|
+
it 'inserts value before first' do
|
332
|
+
list.push('foo')
|
333
|
+
list.push('bar')
|
334
|
+
node = list.each_node.find { |n| n.data == 'foo' }
|
335
|
+
assert_equal 1, list.insert_before_node(1, node).data
|
336
|
+
assert_equal [1, 'foo', 'bar'], list.to_a
|
337
|
+
assert_equal 3, list.length
|
338
|
+
end
|
339
|
+
|
340
|
+
describe 'position edge cases' do
|
341
|
+
before do
|
342
|
+
list.push(0)
|
343
|
+
list.push(1)
|
344
|
+
list.push(2)
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'inserts before head' do
|
348
|
+
node = list.each_node.find { |n| n.data == 0 }
|
349
|
+
list.insert_before_node('foo', node)
|
350
|
+
assert_equal ['foo', 0, 1, 2], list.to_a
|
351
|
+
assert_equal 'foo', list.first
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'inserts before in the middle' do
|
355
|
+
node = list.each_node.find { |n| n.data == 2 }
|
356
|
+
list.insert_before_node('foo', node)
|
357
|
+
assert_equal [0, 1, 'foo', 2], list.to_a
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe '#delete' do
|
363
|
+
it 'raises error if block and value are passed' do
|
364
|
+
err = assert_raises ArgumentError do
|
365
|
+
assert_nil list.delete('x') { |d| d == 'x' }
|
366
|
+
end
|
367
|
+
assert_equal err.message, 'either value or block should be passed'
|
368
|
+
end
|
369
|
+
|
370
|
+
describe 'by block' do
|
371
|
+
it 'returns nil if list is empty' do
|
372
|
+
assert_nil list.delete { |d| d == 'x' }
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'deletes value in first matching node' do
|
376
|
+
calls_count = 0
|
377
|
+
list.push('foo')
|
378
|
+
list.push('foo')
|
379
|
+
list.push('bar')
|
380
|
+
list.push('foo')
|
381
|
+
list.delete { |d| calls_count += 1;d == 'bar' }
|
382
|
+
assert_equal ['foo', 'foo', 'foo'], list.to_a
|
383
|
+
assert_equal 3, calls_count
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'returns deleted value' do
|
387
|
+
list.push('foo')
|
388
|
+
assert_equal 'foo', list.delete { |d| d == 'foo' }
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'decreases length of list' do
|
392
|
+
list.push('foo')
|
393
|
+
list.push('bar')
|
394
|
+
list.push('foo')
|
395
|
+
list.delete { |d| d == 'foo' }
|
396
|
+
assert_equal 2, list.length
|
397
|
+
assert_equal ['bar', 'foo'], list.to_a
|
398
|
+
end
|
399
|
+
|
400
|
+
describe 'position edge cases' do
|
401
|
+
before do
|
402
|
+
list.push(0)
|
403
|
+
list.push(1)
|
404
|
+
list.push(2)
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'deletes value from head' do
|
408
|
+
list.delete { |d| d == 0 }
|
409
|
+
assert_equal [1, 2], list.to_a
|
410
|
+
assert_equal 1, list.first
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'deletes value from middle' do
|
414
|
+
list.delete { |d| d == 1 }
|
415
|
+
assert_equal [0, 2], list.to_a
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'deletes value from tail' do
|
419
|
+
list.delete { |d| d == 2 }
|
420
|
+
assert_equal [0, 1], list.to_a
|
421
|
+
assert_equal 1, list.last
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
describe 'by data equality' do
|
427
|
+
it 'returns nil if list is empty' do
|
428
|
+
assert_nil list.delete('x')
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'deletes value in first node' do
|
432
|
+
list.push('foo')
|
433
|
+
list.push('foo')
|
434
|
+
list.push('bar')
|
435
|
+
list.push('foo')
|
436
|
+
list.delete('foo')
|
437
|
+
assert_equal ['foo', 'bar', 'foo'], list.to_a
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'returns deleted value' do
|
441
|
+
list.push('foo')
|
442
|
+
assert_equal 'foo', list.delete('foo')
|
443
|
+
end
|
444
|
+
|
445
|
+
it 'decreases length of list' do
|
446
|
+
list.push('foo')
|
447
|
+
list.push('bar')
|
448
|
+
list.push('foo')
|
449
|
+
list.delete('foo')
|
450
|
+
assert_equal 2, list.length
|
451
|
+
assert_equal ['bar', 'foo'], list.to_a
|
452
|
+
end
|
453
|
+
|
454
|
+
describe 'position edge cases' do
|
455
|
+
before do
|
456
|
+
list.push(0)
|
457
|
+
list.push(1)
|
458
|
+
list.push(2)
|
459
|
+
end
|
460
|
+
|
461
|
+
it 'deletes value from head' do
|
462
|
+
list.delete(0)
|
463
|
+
assert_equal [1, 2], list.to_a
|
464
|
+
assert_equal 1, list.first
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'deletes value from middle' do
|
468
|
+
list.delete(1)
|
469
|
+
assert_equal [0, 2], list.to_a
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
it 'deletes value from tail' do
|
474
|
+
list.delete(2)
|
475
|
+
assert_equal [0, 1], list.to_a
|
476
|
+
assert_equal 1, list.last
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
describe 'by node' do
|
482
|
+
it 'deletes first node' do
|
483
|
+
list.push(node_1)
|
484
|
+
list.push(node_2)
|
485
|
+
list.push(node_3)
|
486
|
+
list.delete(node_1)
|
487
|
+
assert_equal ['bar', 'baz'], list.to_a
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'returns deleted value' do
|
491
|
+
list.push(node_1)
|
492
|
+
list.delete(node_1)
|
493
|
+
assert_equal node_1.data, list.delete(node_1)
|
494
|
+
end
|
495
|
+
|
496
|
+
it 'decreases length of list' do
|
497
|
+
list.push('foo')
|
498
|
+
list.push('bar')
|
499
|
+
list.push('baz')
|
500
|
+
list.delete('foo')
|
501
|
+
assert_equal 2, list.length
|
502
|
+
end
|
503
|
+
|
504
|
+
describe 'position edge cases' do
|
505
|
+
before do
|
506
|
+
list.push(node_1)
|
507
|
+
list.push(node_2)
|
508
|
+
list.push(node_3)
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'deletes value from head' do
|
512
|
+
list.delete(node_1)
|
513
|
+
assert_equal [node_2.data, node_3.data], list.to_a
|
514
|
+
assert_equal node_2.data, list.first
|
515
|
+
assert_equal node_3.data, list.last
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'deletes value from middle' do
|
519
|
+
list.delete(node_2)
|
520
|
+
assert_equal [node_1.data, node_3.data], list.to_a
|
521
|
+
assert_equal node_1.data, list.first
|
522
|
+
assert_equal node_3.data, list.last
|
523
|
+
end
|
524
|
+
|
525
|
+
|
526
|
+
it 'deletes value from tail' do
|
527
|
+
list.delete(node_3)
|
528
|
+
assert_equal [node_1.data, node_2.data], list.to_a
|
529
|
+
assert_equal node_1.data, list.first
|
530
|
+
assert_equal node_2.data, list.last
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
describe 'delete edge cases' do
|
535
|
+
it 'resets original list state when deleting the last node of the list' do
|
536
|
+
assert_nil list.first
|
537
|
+
assert_nil list.last
|
538
|
+
|
539
|
+
list.push(node_1)
|
540
|
+
assert_equal node_1.data, list.first
|
541
|
+
assert_equal node_1.data, list.last
|
542
|
+
|
543
|
+
list.delete(node_1)
|
544
|
+
assert_nil list.first
|
545
|
+
assert_nil list.last
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
describe '#delete_all' do
|
552
|
+
it 'raises error if block and value are passed' do
|
553
|
+
err = assert_raises ArgumentError do
|
554
|
+
assert_nil list.delete_all('x') { |d| d == 'x' }
|
555
|
+
end
|
556
|
+
assert_equal err.message, 'either value or block should be passed'
|
557
|
+
end
|
558
|
+
|
559
|
+
describe 'by block' do
|
560
|
+
it 'returns nil if list is empty' do
|
561
|
+
assert_equal list.delete_all { |d| d == 'x' }, []
|
562
|
+
end
|
563
|
+
|
564
|
+
it 'deletes value in first matching node' do
|
565
|
+
list.push('foo')
|
566
|
+
list.push('foo')
|
567
|
+
list.push('bar')
|
568
|
+
list.push('foo')
|
569
|
+
list.delete_all { |d| d == 'foo' }
|
570
|
+
assert_equal ['bar'], list.to_a
|
571
|
+
end
|
572
|
+
|
573
|
+
it 'returns deleted value' do
|
574
|
+
list.push('foo')
|
575
|
+
assert_equal ['foo'], list.delete_all { |d| d == 'foo' }
|
576
|
+
end
|
577
|
+
|
578
|
+
it 'decreases length of list' do
|
579
|
+
list.push('foo')
|
580
|
+
list.push('bar')
|
581
|
+
list.push('foo')
|
582
|
+
list.delete_all { |d| d == 'foo' }
|
583
|
+
assert_equal 1, list.length
|
584
|
+
assert_equal ['bar'], list.to_a
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
describe 'by data equality' do
|
589
|
+
it 'returns nil if list is empty' do
|
590
|
+
assert_nil list.delete('x')
|
591
|
+
end
|
592
|
+
|
593
|
+
it 'deletes all matched values' do
|
594
|
+
list.push('foo')
|
595
|
+
list.push('foo')
|
596
|
+
list.push('bar')
|
597
|
+
list.push('foo')
|
598
|
+
list.delete_all('foo')
|
599
|
+
assert_equal ['bar'], list.to_a
|
600
|
+
end
|
601
|
+
|
602
|
+
it 'returns deleted value' do
|
603
|
+
list.push('foo')
|
604
|
+
assert_equal ['foo'], list.delete_all('foo')
|
605
|
+
end
|
606
|
+
|
607
|
+
it 'decreases length of list' do
|
608
|
+
list.push('foo')
|
609
|
+
list.push('bar')
|
610
|
+
list.push('foo')
|
611
|
+
list.delete_all('foo')
|
612
|
+
assert_equal 1, list.length
|
613
|
+
assert_equal ['bar'], list.to_a
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
137
618
|
describe '#shift' do
|
138
619
|
it 'returns nil if list is empty' do
|
139
620
|
assert_nil list.shift
|
@@ -200,6 +681,21 @@ describe LinkedList::List do
|
|
200
681
|
end
|
201
682
|
end
|
202
683
|
|
684
|
+
describe '#each_node' do
|
685
|
+
it 'returns enumerator if no block given' do
|
686
|
+
assert_instance_of Enumerator, list.each_node
|
687
|
+
end
|
688
|
+
|
689
|
+
it 'pass each node data to the block' do
|
690
|
+
list.push(node_1)
|
691
|
+
list.push(node_2)
|
692
|
+
nodes = []
|
693
|
+
list.each_node { |e| nodes << e }
|
694
|
+
assert_equal %w(foo bar), nodes.map(&:data)
|
695
|
+
assert_equal true, nodes.all? { |n| n.is_a?(LinkedList::Node) }
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
203
699
|
describe '#each' do
|
204
700
|
it 'returns enumerator if no block given' do
|
205
701
|
assert_instance_of Enumerator, list.each
|
@@ -214,6 +710,48 @@ describe LinkedList::List do
|
|
214
710
|
end
|
215
711
|
end
|
216
712
|
|
713
|
+
describe '#each_node' do
|
714
|
+
it 'returns enumerator if no block given' do
|
715
|
+
assert_instance_of Enumerator, list.each
|
716
|
+
end
|
717
|
+
|
718
|
+
it 'pass each node data to the block' do
|
719
|
+
list.push(node_1)
|
720
|
+
list.push(node_2)
|
721
|
+
nodes = []
|
722
|
+
list.each_node { |e| nodes << e }
|
723
|
+
assert_equal %w(foo bar), nodes.map(&:data)
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
describe '#reverse_each' do
|
728
|
+
it 'returns enumerator if no block given' do
|
729
|
+
assert_instance_of Enumerator, list.each
|
730
|
+
end
|
731
|
+
|
732
|
+
it 'pass each node data to the block' do
|
733
|
+
list.push(node_1)
|
734
|
+
list.push(node_2)
|
735
|
+
nodes = []
|
736
|
+
list.reverse_each { |e| nodes << e }
|
737
|
+
assert_equal %w(bar foo), nodes
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
describe '#reverse_each_node' do
|
742
|
+
it 'returns enumerator if no block given' do
|
743
|
+
assert_instance_of Enumerator, list.each
|
744
|
+
end
|
745
|
+
|
746
|
+
it 'pass each node data to the block' do
|
747
|
+
list.push(node_1)
|
748
|
+
list.push(node_2)
|
749
|
+
nodes = []
|
750
|
+
list.reverse_each_node { |e| nodes << e }
|
751
|
+
assert_equal %w(bar foo), nodes.map(&:data)
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
217
755
|
describe '#inspect' do
|
218
756
|
it 'includes class name' do
|
219
757
|
assert_match(/LinkedList::List/, list.inspect)
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,49 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linked-list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yury Velikanau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '2.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.0'
|
20
23
|
type: :development
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: coveralls
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
25
45
|
- !ruby/object:Gem::Version
|
26
46
|
version: '0'
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
48
|
+
name: m
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
30
50
|
requirements:
|
31
|
-
- - "
|
51
|
+
- - "~>"
|
32
52
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
34
|
-
- - "
|
53
|
+
version: '1.5'
|
54
|
+
- - ">="
|
35
55
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
56
|
+
version: 1.5.0
|
37
57
|
type: :development
|
38
58
|
prerelease: false
|
39
59
|
version_requirements: !ruby/object:Gem::Requirement
|
40
60
|
requirements:
|
41
|
-
- - "
|
61
|
+
- - "~>"
|
42
62
|
- !ruby/object:Gem::Version
|
43
|
-
version: '1.
|
44
|
-
- - "
|
63
|
+
version: '1.5'
|
64
|
+
- - ">="
|
45
65
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
66
|
+
version: 1.5.0
|
47
67
|
- !ruby/object:Gem::Dependency
|
48
68
|
name: minitest
|
49
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -65,19 +85,19 @@ dependencies:
|
|
65
85
|
- !ruby/object:Gem::Version
|
66
86
|
version: '6.0'
|
67
87
|
- !ruby/object:Gem::Dependency
|
68
|
-
name:
|
88
|
+
name: rake
|
69
89
|
requirement: !ruby/object:Gem::Requirement
|
70
90
|
requirements:
|
71
91
|
- - ">="
|
72
92
|
- !ruby/object:Gem::Version
|
73
|
-
version:
|
93
|
+
version: 12.3.3
|
74
94
|
type: :development
|
75
95
|
prerelease: false
|
76
96
|
version_requirements: !ruby/object:Gem::Requirement
|
77
97
|
requirements:
|
78
98
|
- - ">="
|
79
99
|
- !ruby/object:Gem::Version
|
80
|
-
version:
|
100
|
+
version: 12.3.3
|
81
101
|
description: Ruby implementation of Doubly Linked List, following some Ruby idioms.
|
82
102
|
email:
|
83
103
|
- yury.velikanau@gmail.com
|
@@ -89,6 +109,7 @@ extra_rdoc_files: []
|
|
89
109
|
files:
|
90
110
|
- ".gitignore"
|
91
111
|
- ".travis.yml"
|
112
|
+
- CHANGELOG.md
|
92
113
|
- Gemfile
|
93
114
|
- LICENSE.txt
|
94
115
|
- README.md
|
@@ -124,8 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
145
|
- !ruby/object:Gem::Version
|
125
146
|
version: '0'
|
126
147
|
requirements: []
|
127
|
-
|
128
|
-
rubygems_version: 2.7.6
|
148
|
+
rubygems_version: 3.1.2
|
129
149
|
signing_key:
|
130
150
|
specification_version: 4
|
131
151
|
summary: Ruby implementation of Doubly Linked List, following some Ruby idioms.
|