Array#delete
arr.delete(obj) -> obj or nil The delete method removes all elements from an array that match a given object. Unlike delete_at which removes by index, delete searches by value and removes every occurrence. It returns the deleted value, or nil if nothing was found.
That behavior is useful when the value itself is the thing you want to remove and you do not care where it appears. It is a direct fit for cleanup code, list maintenance, and any place where duplicate matches should all disappear at once.
Key takeaways
deleteremoves elements by value, not by index.- It removes every matching value in one pass.
- The return value is the deleted object, which can help you confirm what was removed.
- Use the block form only when you want a fallback message or value.
- Choose
delete_atinstead when the array position matters.
fruits = ["apple", "banana", "cherry", "banana"]
fruits.delete("banana")
# => "banana"
fruits
# => ["apple", "cherry"]
The code above shows the basic pattern: call delete with the value you want gone, and every occurrence of that value is removed. The return value is the matched object itself, which lets you confirm what was deleted without inspecting the array afterward. The array is modified in place, so the original reference now holds the cleaned-up version.
Syntax
arr.delete(obj)
arr.delete(obj) { block }
The first form removes every occurrence of obj and returns the deleted value. The second form works the same way but provides a fallback block that runs only when no match is found, which gives the caller a chance to supply a default message or value instead of getting back nil.
Parameters
| Parameter | Type | Description |
|---|---|---|
obj | Object | The value to remove from the array |
| block | Block | Optional block evaluated if no match is found |
Return Value
delete returns the deleted object if found, or nil if no matching elements exist. The block, if provided, is only evaluated when nothing is deleted:
arr = [1, 2, 3]
# Returns the deleted value
arr.delete(2)
# => 2
arr
# => [1, 3]
# Returns nil when not found (no block)
arr = [1, 2, 3]
arr.delete(99)
# => nil
# Block runs only when no match
arr = [1, 2, 3]
arr.delete(99) { "not found" }
# => "not found"
The return value follows a simple rule: when a match is found, the deleted object comes back; when nothing matches and no block is given, nil comes back; and when nothing matches but a block is present, the block’s return value is used instead. This three-way behaviour covers the common cases without forcing the caller to check the array separately.
Removing multiple occurrences
delete removes all elements equal to the given object:
numbers = [1, 2, 3, 2, 4, 2]
numbers.delete(2)
# => 2
numbers
# => [1, 3, 4]
# Every "banana" is removed
items = ["a", "b", "a", "c", "a"]
items.delete("a")
# => "a"
items
# => ["b", "c"]
The fact that delete removes every match makes it especially useful for de-duplication and cleanup tasks where multiple copies of the same value should all disappear at once. It is less helpful when you only want to remove the first occurrence, in which case you would need to find the index and use delete_at instead.
Common use cases
Cleaning user input
# Remove unwanted values from user-submitted data
tags = ["ruby", "python", "", "java", nil, "ruby"]
tags.delete("")
# => ""
tags
# => ["ruby", "python", "java", nil, "ruby"]
tags.delete(nil)
# => nil
tags
# => ["ruby", "python", "java", "ruby"]
Cleaning up user input often involves removing sentinel values like empty strings, nils, or placeholder entries before passing the data on to the next stage of processing. Calling delete for each unwanted value keeps the cleanup logic linear and easy to read, since each call states exactly what is being removed.
Removing specific items
# Filter out processed items
queue = ["task1", "task2", "task1", "task3"]
completed = "task1"
queue.delete(completed)
queue
# => ["task2", "task3"]
When the array holds simple values like strings or symbols, delete works directly because Ruby already knows how to compare those types. The call is short and the intent is obvious: remove every entry equal to the given item and carry on with the remaining elements.
With object comparison
# Remove objects by custom equality
class Task
attr_reader :name, :done
def initialize(name, done: false)
@name = name
@done = done
end
def ==(other)
other.is_a?(Task) && name == other.name
end
end
tasks = [Task.new("buy milk"), Task.new("clean room"), Task.new("buy milk")]
tasks.delete(Task.new("buy milk"))
# => #<Task:0x00007f8a2c0c1a40 @name="buy milk", @done=false>
tasks
# => [#<Task:0x00007f8a2c0c1a38 @name="clean room">]
Comparing with other methods
| Method | Searches By | Removes | Returns |
|---|---|---|---|
delete | value | All matches | Deleted value or nil |
delete_at | index | Single element | Deleted element or nil |
select! | block | Multiple (keep truthy) | self or nil |
reject! | block | Multiple (remove truthy) | self or nil |
compact | - | nil values | new array |
arr = [1, 2, 3, 2, 4]
# delete by value
arr.delete(2)
# => 2
arr
# => [1, 3, 4]
The array above started with [1, 2, 3, 2, 4] and after deleting 2 only [1, 3, 4] remains. All occurrences of 2 were removed in a single pass, which is why the array shrank by two elements rather than one.
arr = [1, 2, 3, 2, 4]
# delete_at by index
arr.delete_at(1)
# => 2
arr
# => [1, 3, 2, 4]
The main thing to keep in mind is that delete is value-based, so it is closer to a search-and-remove operation than an indexed edit. If the array position is important, use delete_at. If the value is the thing that matters, delete keeps the call site simple.
Common mistakes
One common mistake is expecting delete to remove only the first matching value. It removes every matching element, so it is best for cleanup or de-duplication, not for targeted positional edits.
Another mistake is relying on the return value to tell you whether the array changed. If the deleted object could also be present in the array for another reason, the return value alone is not enough. Check the array after the call if you need to confirm the final state.
Conclusion
delete is a small but practical array method when you want to remove every matching value from a collection. It keeps the call site simple, works well for cleanup tasks, and makes the intent obvious when compared with index-based deletion.
Gotchas
Using the block for defaults
The block only executes when no match is found, not when the value is nil:
# This is a common mistake
arr = [1, nil, 2]
arr.delete(nil) { "default" }
# => nil (not "default")
# Because nil was actually found and deleted
This is a subtle point: the block runs only when no matching element exists in the array, not when the matched element happens to be falsy. A nil in the array is still a match, so delete(nil) succeeds and returns nil without ever calling the block. The block is strictly a fallback for the no-match case.
Reference Equality
Ruby uses == for comparison, not necessarily equal?:
# Different objects but equal values
"hello" == "hello"
# => true
arr = ["hello"]
arr.delete("hello")
# => "hello" (deleted!)
Ruby uses the == operator for comparison by default, which means different string objects with the same content are treated as equal. This is what you usually want, but it is worth keeping in mind when working with custom objects that define their own equality semantics. The comparison behaviour is consistent with how Ruby handles equality everywhere else in the language.
Return value after deletion
If you need the deleted value and the modified array, note that delete returns the value, not the array:
arr = [1, 2, 3]
deleted = arr.delete(2)
# => 2 (the value, not [1, 3])
# If you need both:
arr = [1, 2, 3]
deleted = arr[arr.index(2)]
arr.delete(2)
# => 2, arr is now [1, 3]
The returned value can be handy when you want to confirm what was removed, but in most cases the mutated array is the real result. That is why many callers use delete for its side effect and ignore the return value entirely.
See Also
Array#compact— Remove nil values from arraysArray#select— Keep elements matching a condition