[arrays] How can I delete one element from an array by value

I have an array of elements in Ruby

[2,4,6,3,8]

I need to remove elements with value 3 for example

How do I do that?

This question is related to arrays ruby

The answer is


So when you have multiple occurrences of 3 and you want only to delete the first occurrence of 3, you can simply do some thing as below.

arr = [2, 4, 6, 3, 8, 10, 3, 12]

arr.delete_at arr.index 3

#This will modify arr as [2, 4, 6, 8, 10, 3, 12] where first occurrence of 3 is deleted. Returns the element deleted. In this case => 3.

I like the -=[4] way mentioned in other answers to delete the elements whose value is 4.

But there is this way:

[2,4,6,3,8,6].delete_if { |i| i == 6 }
=> [2, 4, 3, 8]

mentioned somewhere in "Basic Array Operations", after it mentions the map function.


Here are some benchmarks:

require 'fruity'


class Array          
  def rodrigo_except(*values)
    self - values
  end    

  def niels_except value
    value = value.kind_of?(Array) ? value : [value]
    self - value
  end
end

ARY = [2,4,6,3,8]

compare do
  soziev  { a = ARY.dup; a.delete(3);               a }
  steve   { a = ARY.dup; a -= [3];                  a }
  barlop  { a = ARY.dup; a.delete_if{ |i| i == 3 }; a }
  rodrigo { a = ARY.dup; a.rodrigo_except(3);         }
  niels   { a = ARY.dup; a.niels_except(3);           }
end

# >> Running each test 4096 times. Test will take about 2 seconds.
# >> soziev is similar to barlop
# >> barlop is faster than steve by 2x ± 1.0
# >> steve is faster than rodrigo by 4x ± 1.0
# >> rodrigo is similar to niels

And again with a bigger array containing lots of duplicates:

class Array          
  def rodrigo_except(*values)
    self - values
  end    

  def niels_except value
    value = value.kind_of?(Array) ? value : [value]
    self - value
  end
end

ARY = [2,4,6,3,8] * 1000

compare do
  soziev  { a = ARY.dup; a.delete(3);               a }
  steve   { a = ARY.dup; a -= [3];                  a }
  barlop  { a = ARY.dup; a.delete_if{ |i| i == 3 }; a }
  rodrigo { a = ARY.dup; a.rodrigo_except(3);         }
  niels   { a = ARY.dup; a.niels_except(3);           }
end

# >> Running each test 16 times. Test will take about 1 second.
# >> steve is faster than soziev by 30.000000000000004% ± 10.0%
# >> soziev is faster than barlop by 50.0% ± 10.0%
# >> barlop is faster than rodrigo by 3x ± 0.1
# >> rodrigo is similar to niels

And even bigger with more duplicates:

class Array          
  def rodrigo_except(*values)
    self - values
  end    

  def niels_except value
    value = value.kind_of?(Array) ? value : [value]
    self - value
  end
end

ARY = [2,4,6,3,8] * 100_000

compare do
  soziev  { a = ARY.dup; a.delete(3);               a }
  steve   { a = ARY.dup; a -= [3];                  a }
  barlop  { a = ARY.dup; a.delete_if{ |i| i == 3 }; a }
  rodrigo { a = ARY.dup; a.rodrigo_except(3);         }
  niels   { a = ARY.dup; a.niels_except(3);           }
end

# >> Running each test once. Test will take about 6 seconds.
# >> steve is similar to soziev
# >> soziev is faster than barlop by 2x ± 0.1
# >> barlop is faster than niels by 3x ± 1.0
# >> niels is similar to rodrigo

Borrowing from Travis in the comments, this is a better answer:

I personally like [1, 2, 7, 4, 5] - [7] which results in => [1, 2, 4, 5] from irb

I modified his answer seeing that 3 was the third element in his example array. This could lead to some confusion for those who don't realize that 3 is in position 2 in the array.


Another option:

a = [2,4,6,3,8]

a -= [3]

which results in

=> [2, 4, 6, 8] 

Compiling all the different options for delete in ruby

delete - Deletes matching elements by value. If more than one value matches it will remove all. If you don't care about the number of occurrence or sure about single occurrence, use this method.

a = [2, 6, 3, 5, 3, 7]
a.delete(3)  # returns 3
puts a       # return [2, 6, 5, 7]

delete_at - Deletes element at given index. If you know the index use this method.

# continuing from the above example
a.delete_at(2) # returns 5
puts a         # returns [2, 6, 7]

delete_if - Deletes every element for which block is true. This will modify the array. Array changes instantly as the block is called.

b = [1, 2, 5, 4, 9, 10, 11]
b.delete_if {|n| n >= 10}.  # returns [1, 2, 5, 4, 9]

reject - This will return new array with the elements for which the given block is false. The ordering is maintained with this.

c = [1, 2, 5, 4, 9, 10, 11]
c.reject {|n| n >= 10}.  # returns [1, 2, 5, 4, 9]

reject! - same as delete_if. Array may not change instantly as the block is called.

If you want to delete multiple values from array, the best option is as bellow.

a = [2, 3, 7, 4, 6, 21, 13]
b = [7, 21]
a = a - b    # a - [2, 3, 4, 6, 13]

A .delete_at(3) 3 here being the position.


Assuming you want to delete 3 by value at multiple places in an array, I think the ruby way to do this task would be to use the delete_if method:

[2,4,6,3,8,3].delete_if {|x| x == 3 } 

You can also use delete_if in removing elements in the scenario of 'array of arrays'.

Hope this resolves your query


If you also want to make this deletion operation chainable, so you can delete some item and keep on chaining operations on the resulting array, use tap:

[2, 4, 6, 3, 8].tap { |ary| ary.delete(3) }.count #=> 4

You can also monkey patch it. I never understood why Ruby has an except method for Hash but not for Array:

class Array
  def except value
    value = value.kind_of(Array) ? value : [value]
    self - value
  end
end

Now you can do:

[1,3,7,"436",354,nil].except(354) #=> [1,3,7,"436",nil]

Or:

[1,3,7,"436",354,nil].except([354, 1]) #=> [3,7,"436",nil]

I'm not sure if anyone has stated this, but Array.delete() and -= value will delete every instance of the value passed to it within the Array. In order to delete the first instance of the particular element you could do something like

arr = [1,3,2,44,5]
arr.delete_at(arr.index(44))

#=> [1,3,2,5]

There could be a simpler way. I'm not saying this is best practice, but it is something that should be recognized.


Non-destructive removal of first occurrence:

a = [2, 4, 6, 3, 8]
n = a.index 3
a.take(n)+a.drop(n+1)

You can simply run:

[2,4,6,3,8].delete(3)

I improved Niels's solution

class Array          
  def except(*values)
    self - values
  end    
end

Now you can use

[1, 2, 3, 4].except(3, 4) # return [1, 2]
[1, 2, 3, 4].except(4)    # return [1, 2, 3]