linked-list 0.0.10 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15032f04d676c41f8dedbb0d911c82a35944826d5469c3a32530548fc55efa5c
4
- data.tar.gz: 3f8b4bab1f5d9dca9f88b2e4e9baa78b2e9120eaf41ad7c3095e4072b7609be9
3
+ metadata.gz: a70c658176bc2e924764731304a3f8dbb376306e35b9e016d41896a7b5790463
4
+ data.tar.gz: cde342c544b41d53f5c52eb3d592deed28a6bf79736b6a0f2dbb1ef2d78319a7
5
5
  SHA512:
6
- metadata.gz: c174a97b29c738a4224025639b744c97f086f0cc80afe21f20678ea708be29ff956fd8e81feb4f606d7a1ac9376f2fb334afe7bd730e3b8263af69107255bc50
7
- data.tar.gz: 0eeb55908b8b9ddeb7f477262899e1a8d990e013af21d0c723c64a9c92248b4a1964e48450f379de1642906c0f68e08ea4a7f20dda360e45346a4ce9dd057a4d
6
+ metadata.gz: db29d3d0ec5195c12937ae63332a8dbb3ac891f6510441762f5a42d1acfce8de826d9a20a6af308e9b39a5fe33fcf92527c26d1ffd757c6814fb2c09ed4c34a4
7
+ data.tar.gz: 7723a09dc0858687eaf4ff6668e024ba8b7efd00cc202ab6eb067baffc52f645f6138d286cec946ccb30896b372345f91b601db5e8dee8900c65582ffe61577b
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
3
+ - 2.7
5
4
  cache: bundler
5
+ env:
6
+ CI: true
@@ -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.each # Enumerator object
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.first # head of the list
51
- list.last # tail of the 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 # same as `length`
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
- case arg
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
- case arg
35
- when ->(_arg) { _arg.respond_to?(:to_list) }
33
+ if arg.respond_to?(:to_list)
36
34
  arg.to_list
37
- when ->(_arg) { _arg.respond_to?(:to_ary) }
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))
@@ -4,7 +4,7 @@ module LinkedList
4
4
  class List
5
5
  include Conversions
6
6
 
7
- attr_reader :length
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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Linked
4
4
  module List
5
- VERSION = '0.0.10'
5
+ VERSION = '0.0.15'
6
6
  end
7
7
  end
@@ -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{Ruby implementation of Doubly Linked List, following some Ruby idioms.}
12
- spec.summary = %q{Ruby implementation of Doubly Linked List, following some Ruby idioms.}
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 'rake'
22
- spec.add_development_dependency 'bundler', '>= 1.3', '<= 2.0'
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 'coveralls'
25
+ spec.add_development_dependency 'rake', '>= 12.3.3'
25
26
  end
@@ -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)
@@ -1,5 +1,7 @@
1
- require 'coveralls'
2
- Coveralls.wear!
1
+ if ENV['CI']
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+ end
3
5
 
4
6
  require 'linked-list'
5
7
 
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.10
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: 2018-04-03 00:00:00.000000000 Z
11
+ date: 2020-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rake
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: bundler
48
+ name: m
29
49
  requirement: !ruby/object:Gem::Requirement
30
50
  requirements:
31
- - - ">="
51
+ - - "~>"
32
52
  - !ruby/object:Gem::Version
33
- version: '1.3'
34
- - - "<="
53
+ version: '1.5'
54
+ - - ">="
35
55
  - !ruby/object:Gem::Version
36
- version: '2.0'
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.3'
44
- - - "<="
63
+ version: '1.5'
64
+ - - ">="
45
65
  - !ruby/object:Gem::Version
46
- version: '2.0'
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: coveralls
88
+ name: rake
69
89
  requirement: !ruby/object:Gem::Requirement
70
90
  requirements:
71
91
  - - ">="
72
92
  - !ruby/object:Gem::Version
73
- version: '0'
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: '0'
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
- rubyforge_project:
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.