Ruby: Delete and return an array value based on block find condition

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]

Array#extract (Rails 6+)

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]

You can use partition. Obviously the block example here doesn't make complete sense but returns both deleted items and left over.

a = [1,2,3,4,5,6,7,8,9]
b, a = a.partition { |x| [1,3,5].include?(x) }

b # [1, 3, 5]
a # [2, 4, 6, 7, 8, 9]

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]