Remove multiple array elements by indices
The main concern with this implementation is not its complexity but the fact that you transform ranges into arrays of indices. It works fine for basic cases, but what if the range is huge? For example,

array_except(a, 1..100000000)


works with the significant delay on my laptop already, and infinite range will obviously cause your implementation to never terminate.

But you don't need to iterate over all the numbers within the range to check if the index belongs to it, right? It's enough to just check the boundaries. So, my suggestion is

def array_except(array, *indexes)
  array.reject.with_index do |_, i|
    indexes.any? do |index|
      index.is_a?(Range) ? index.cover?(i) : index == i
    end
  end
end

pry(main)&gt; array_except(a, 1..Float::INFINITY) # =&gt; ["a"]


UPD. Please, note: it doesn't work with the negative ranges as is, but can be adjusted if you decide to go in this direction - for example, via converting ranges with the negative boundaries into "equivalent" ones with the proper positive boundaries:

def array_except(array, *indexes)
  array.reject.with_index do |_, i|
    indexes.any? do |index|
      if index.is_a?(Range)
        start = index.begin &gt; 0 ? index.begin : array.length + index.begin
        stop = index.end &gt; 0 ? index.end : array.length + index.end
        start &lt;= i &amp;&amp; i &lt;= stop    
      else
        index == i
      end
    end
  end
end


The final version is more verbose than your initial implementation, but its performance doesn't depend on the size of the ranges provided as an input...

UPD2. If you don't care about the predictable performance and is just interested in a concise implementation, it can be made as short as

def array_except(array, *indexes)
  array.reject.with_index { |_, i| indexes.any? { |ind| Array(ind).include?(i) } }
end


UPD3. Good point raised in the comment. The second implementation works terribly bad in the cases when indexes parameter is a huge list of something (integers or ranges) applied to a large list. In this case we get stuck with O(m*n)... To beat this we need something more sophisticated. For example, we can first iterate over all indexes and fill some auxiliary data structure (with O(1) read access) with some "flags" (defining what to keep/delete) and then iterate over the array itself and clean it up. Very dirty implementation:

def array_except4(array, *indexes)
  to_delete = Array.new(array.size)

  indexes.each do |index|
    case index
    when Range
      start, stop = [index.begin, index.end].sort
      start = start &lt; 0 ? [0, start + array.length].max : [start, array.length - 1].min
      stop = stop &lt; 0 ? [stop + array.length, array.length - 1].min : [stop, array.length - 1].min

      (start..stop).each { |k| to_delete[k] = true }
    else
      to_delete[index] = true
    end
  end

  array.reject.with_index { |_, i| to_delete[i] }
end
```