ruby picture

RCR 285: Array#delete and Array#delete_at added functionality

Submitted by _ibz (Thu Nov 11 04:11:18 UTC 2004)

Abstract

Array#delete deletes *all* items from _self_ that are equal to the object given as it's argument. I propose an enhancement that would allow caller to select the number of objects that are deleted.

Array#delete_at deletes the element at the specified index, returning that element or _nil_ if the index is out of range. I propose an enhancement that would allow caller to specify several indexes, or ranges of indexes.

Problem

Not a problem, just a nicety which *I* personally think would be an "enhancement", (if the usage of the loaded term can be excused) for a seemingly small amount of code.

Proposal

I believe the code demonstrates that the modifications can be achieved without breaking semantics or backward compatibility. However, I suspect there's a cleaner way to write what I have written.

Analysis

Array#values_at can accept an index or a range. I have tried to model Array#delete_at in a similar way, as delete_at should really allow a range of objects to be deleted - the order of the arguments matters however, since we are modifying the array as we process the arguments.

Array#delete can accept a block, and the block is called when the object is not found. I have tried to model this same behaviour, but when a number of objects specified to be deleted exceeds the number available, then the block will be called once for each time the object is not found.

Implementation

class Array
  alias old_delete_at delete_at
  def delete_at(*elements)
    result = elements.collect do |n|
      case
      when !n.is_a?(Integer) && !n.is_a?(Range)
        raise TypeError, "argument should be Integer or Range"
      when n.is_a?(Integer)
        old_delete_at(n)
      when n.is_a?(Range)
        slice!(n)
      end
    end
    return nil if result.empty?
    return result[0] if result.size == 1
    return result.flatten
  end
  alias old_delete delete
  def delete(element, n = nil, &block)
    case
    when n == nil
      old_delete(element, &block)
    when !n.is_a?(Integer)
      raise TypeError, "second argument should be Integer or nil!"
    when n < 0
      result = []
      (-n).times do
        found = rindex(element)
        if found.nil?
          block.call(n) if block_given?
        else
          result << slice!(rindex(element))
        end
      end
      result
    when n > 0
      result = []
      n.times do
        found = rindex(element)
        if found.nil?
          block.call(n) if block_given?
        else
          result << slice!(found)
        end
      end
      result
    end
  end
end

ruby picture
Comments Current voting
I agree this would be a nice feature to have.


For #delete_at try this:

  def delete_values_at(*selectors)
    idx = []
    selectors.each{ |i|
      case i
      when Range
        idx.concat( i.to_a )
      else
        idx << i.to_i
      end
    }
    idx.uniq!
    dvals = values_at(*idx)
    idx = (0...size).to_a - idx
    self.replace( values_at(*idx) )
    return dvals
  end

I only recently put this together, so its not tried and true, but my quick testcase is working and I think it handles the need better. Improvements welcome.

I agree though, it might sometimes be helpful if #delete took an optional max number of values to delete.

T.


Strongly opposed 0
Opposed 0
Neutral 0
In favor 3
Strongly advocate 0
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .