42

Is there a built-in method which performs the same function as Array#delete but returns self? I'd like to do it without using a block and clearer than an_ary.-([el]).

I could monkeypatch one, but it seems like a "compact with arguments" method would be a relatively common desire?

8
  • Possible duplicate of stackoverflow.com/questions/5094283/… Commented Apr 6, 2011 at 20:18
  • Nearly. I'm just wondering if there is a built-in method, really. I'll clarify a bit. Commented Apr 6, 2011 at 20:22
  • 1
    ary.delete_if{ |e| e == elem } isn't very clear or concise. Why bother having Array#compact if we can just delete_if{ |e| e.nil? }? Commented Apr 6, 2011 at 20:27
  • 4
    @fl00r In that case, use an_ary-=[el] Commented Apr 6, 2011 at 20:40
  • 1
    @sawa it's not the method name I dislike, it's requiring the argument to also be an array. It can potentially get confusing when I'm trying to remove an element which is also an array, like ary.-([[1, 2]]). Ideally reject, reject!, delete, and delete_if would be brought further in line with the other destructive and synonymous methods. Commented Apr 6, 2011 at 21:01

7 Answers 7

47

If you want to mutate the original array, like delete, here are options:

ary.reject!{|e| e==42 }.something_else
ary.tap{|a| a.delete 42}.something_else
(ary.delete 42;ary).something_else
(ary-=[42]).something_else

If you want a new array to chain from:

ary.reject{|e| e==42 }.something_else
(ary-[42]).something_else
Sign up to request clarification or add additional context in comments.

1 Comment

@gamov Define "overheads" and then you can test and benchmark yourself.
7

an_ary.-([el]) looks awful.

What about...

an_ary - [el]

?

The most natural way of dealing with mathematical operations is this...

4 - 2

Not this...

4.-(2)

4 Comments

It looks a bit ugly...but is the most succinct solution, IMO. It allows chaining, and particularly, it allows chaining with &.
Succinct? an_ary - [el] is more succint on all scenarios. If you don't want to count the spaces, it has 3 less characters. If you want to count the spaces, it has 1 less character. But I don't prefer this solution for succinctness, I prefer for readability/comprehensibility. an_ary - [el] is clear even for new developers (same syntax as python for example), while an_ary.-([el]) is more complex. It just blurs a trivial operation. The other solution allows chaining, but we are not chaining on the first place. I can quote Occam's Razor: “the simplest solution is almost always the best."
Yes, I think you're right, regarding succinctness. My bad.
This worked for me, I was hoping for a method to do this, but ended up with something like: (item[0..6] - [item[1]])
4
array.reject{|element| element == value_of_element_to_be_deleted}

1 Comment

reject - Returns a new array containing the items in self for which the given block is not true. +1
2

You can do

my_array.first(n) #1

my_array.last(n) #2

If the elements of the array you want to delete, are at the end (1) or at the beginning (2) of the array.

2 Comments

I prefer this solution, but don't know about its performance comparing with other solutions though.
Not too keen on this - what happens if someone else changes the array's order at a later date?
1

I had this same question for Array#delete_at that returned an array with the element at a specified index removed, which is why I ended up here. Looks like it isn't built in. In case anyone else is interested, I quickly wrote this monkey patch (I gave this virtually no thought regarding efficiency, and I originally didn't handle any cases such as negative indices or out of bounds indices...but then I decided to quick throw a couple in there):

class Array
  def remove_at(i)
    # handle index out of bounds by returning unchanged array
    return self if i >= self.length

    # remove the i-th element from the end if i is negative
    if i < 0
      i += self.length
      # handle index out of bounds by returning unchanged array
      return self if i < 0
    end

    # Return an array composed of the elements before the specified
    # index plus the elements after the specified index
    return self.first(i) + self.last(self.length - i - 1)
  end
end

test = [0,1,2,3,4,5]
puts test.remove_at(3).inspect
puts test.remove_at(5).inspect
puts test.remove_at(6).inspect
puts test.remove_at(-7).inspect
puts test.remove_at(-2).inspect

I had fun whipping this up, so I figured I might as well post it here :)

Comments

1

I prefer this way:

list = [1, 2, 3, 4, 5]
list.tap { |list| list.delete(2) } # => [1, 3, 4, 5]

Comments

-1

The OP doesn't like the look of an_ary.-([el]) ... but it really does have a lot going for it.

True...it's a little ugly, but the minus method does the trick concisely, subtracting one array from the other:

ary = [1, 2, 99, 3]
ary.-([99])

or

odds = [1, 3, 5, 7, 9, 99]
ary.-(odds)

The advantage here is that it is completely chainable (unlike .delete or ary - odds), so you can do things like:

ary.-(odds).average

Once your eye finds the minus sign, it's much easier to read, understand, and visually spot typos than the .delete_if or .reject block constructs.

It also plays well with Ruby's safe navigation operator, &., if you might get nil instead of an array. That's something you can't do elegantly with subtracting arrays.

maybe_array&.-(odds)&.average

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.