Reputation: 768
For the piece of code below.
I want to remove element which is equals to 3 from an array.
But the code only removed the first element. When I debug the code,
I found that the iterator only loops though the array one time instead of two.
I am not sure what causes the problem. Any help will be appreciated.
nums = [3,3]
def remove_element(nums, val)
nums.each_with_index do |num,index|
if num == val
nums.slice!(index)
end
end
nums.length
end
remove_element(nums,3)
Upvotes: 0
Views: 175
Reputation: 8212
As @steenslag has pointed out, the delete
method will do what you want:
n = [1,2,3,3,4,5,6,3,4,5,3,2,1,8]
n.delete(3)
n
returns: [1, 2, 4, 5, 6, 4, 5, 2, 1, 8]
It's worth looking at this alternative to your code:
nums = [3,3]
def remove_element(nums, val)
nums.each_with_index do |num,index|
nums_before_slice = nums.clone
if num == val
sliced = nums.slice!(index)
end
puts "nums: #{nums_before_slice}, index: #{index}, sliced: #{sliced.inspect}"
end
end
remove_element(nums,3)
puts "Result: #{nums.inspect}"
The output will be:
nums: [3, 3], index: 0, sliced: 3
Result: [3]
As you can see, the iteration only happens once, because the second element has been removed before it is time to do the second iteration.
Compare that result to this version of the code:
nums = [3,3]
def remove_element(nums, val)
nums.clone.each_with_index do |num,index|
nums_before_slice = nums.clone
if num == val
sliced = nums.slice!(index)
end
puts "nums: #{nums_before_slice}, index: #{index}, sliced: #{sliced.inspect}"
end
end
remove_element(nums,3)
puts "Result: #{nums.inspect}"
Which results in:
nums: [3, 3], index: 0, sliced: 3
nums: [3], index: 1, sliced: nil
Result: [3]
This now runs the iteration over a copy of the original nums
, but the result is the same, as on the second iteration - there is no second element to be removed.
Upvotes: 3
Reputation: 4440
What about method delete
?
nums = [3,3]
def remove_element(nums, val)
nums.delete(val)
nums.length
end
remove_element(nums, 3)
#=> 0
or delete_if
?
nums = [3,3]
def remove_element(nums, val)
nums.delete_if { |element| element == val }
nums.length
end
remove_element(nums, 3)
#=> 0
UPD
require 'benchmark'
array = Array.new(100000) { rand(5) }
Benchmark.bm do |x|
x.report("delete: ") { array.delete(5) }
x.report("delete_if: ") { array.delete_if { |e| e == 5 } }
x.report("reject: ") { array.reject! { |e| e == 5 } }
end
# user system total real
# delete: 0.000000 0.000000 0.000000 ( 0.004230)
# delete_if: 0.010000 0.000000 0.010000 ( 0.006387)
# reject: 0.010000 0.000000 0.010000 ( 0.007543)
Upvotes: 2
Reputation:
As rightly commented, you have modified the array while iterating it, due to which it removed the first element, and concluded the iteration. If you put a print statment inside nums.each_with_index
loop, you will see it gets printed only once.
A better way to remove an element can be using reject method like following:
nums.reject!{|item| item == 3}
Upvotes: 3