Is there a built-in way to delete a value from an array, based on a block condition returning true, and return the value that was deleted?
This is a simplified version of what I'm trying to do, but it seems like there has to be a better way:
array = [1,2,3,4,5,6,7,8,9,10]
index = array.index {|v| v == 5} # returns index if block is true
value = array.delete_at(index) # deletes and returns element at index
value
is then 5
Ruby | Array delete() operation Array#delete() : delete() is a Array class method which returns the array after deleting the mentioned elements. It can also delete a particular element in the array. Syntax: Array. delete() Parameter: obj - specific element to delete Return: last deleted values from the array.
The first() is an inbuilt method in Ruby returns an array of first X elements. If X is not mentioned, it returns the first element only. Parameters: The function accepts X which is the number of elements from the beginning. Return Value: It returns an array of first X elements.
split is a String class method in Ruby which is used to split the given string into an array of substrings based on a pattern specified. Here the pattern can be a Regular Expression or a string. If pattern is a Regular Expression or a string, str is divided where the pattern matches.
You can't update the array in place and get a return of a different set of values that are deleted. You can do the following using delete_if
to remove values and capture the ones removed by the logic in the block:
reject = [] => [] content = [1,2,3,4,5,6,7,8,9] => [1, 2, 3, 4, 5, 6, 7, 8, 9] content.delete_if {|v| reject << v if v > 5} => [1, 2, 3, 4, 5] reject => [6, 7, 8, 9] content => [1, 2, 3, 4, 5]
Do you really need to delete items from the original array or are you really just trying to split it into two pieces based on some condition? If the latter, then:
accepted = [ ]
rejected = [ ]
original.each { |e| (want_this_one(e) ? accepted : rejected).push(e) }
or
parts = original.inject({ :accepted => [ ], :rejected => [ ] }) do |accumulator, e|
if(want_this_one(e))
accumulator[:accepted].push(e)
else
accumulator[:rejected].push(e)
end
accumulator
end
And then a simple method wrapper to make it easy to supply a block:
def categorize(array)
categories = array.inject({ :accepted => [ ], :rejected => [ ] }) do |accumulator, e|
if(yield e)
accumulator[:accepted].push(e)
else
accumulator[:rejected].push(e)
end
accumulator
end
return categories[:accepted], categories[:rejected]
end
kept, deleted = categorize([1, 2, 3, 4, 5]) { |n| n % 2 == 0 }
# kept = [2, 4]
# deleted = [1, 3, 5]
Or you could just use Enumerable#partition
to split the array into two pieces.
If you really need to modify the array in-place then this version of Wes's should do the trick:
def slice_out(array)
dead = [ ]
array.delete_if do |e|
if(yield e)
dead.push(e)
true
else
false
end
end
dead
end
a = [1,2,3,4]
x = slice_out(a) { |n| n % 2 == 0 }
# a == [1, 3]
# x == [2, 4]
If you are using Rails, then starting from version 6, there is a method Array#extract!
, which does almost what you need.
It removes and returns the elements for which the block returns a true value and modifies the original array.
Please, have a look at the following example:
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
values = array.extract! { |value| value == 5 }
# array
# => [1, 2, 3, 4, 6, 7, 8, 9, 10]
# values
# => [5]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With