2

I want to get something like this:

a                       =  [0,1,0,0,1,0,1,0]
a.except(1)           # => [0,0,0,1,0,1,0]
a                     # => [0,1,0,0,1,0,1,0]
a.except(1).except(1) # => [0,0,0,0,1,0]

As you see, the first element of a that equals the argument of except is removed from a.

I can do:

tmp_a = a.dup
tmp_a.delete_at(a.index(1))
tmp_a

but in my opinion, it looks like smell. Is there more elegant one-line solution? (In other words - how to define method "except" for Array?)

UPD

I solved this problem so

class Array
  def except(elem)
    dup.tap{|a| a.delete_at(a.index(elem))}
  end
end

what do you think?

7
  • Write a method that encapsulates the element find and slicing. No reason to dupe the array, just take the elements on either side of the value. Commented Jul 6, 2015 at 12:23
  • Meta-comment: down-boring an answer because of a misunderstanding seems extreme to me-just comment to clarify and users will police their own content. Commented Jul 6, 2015 at 12:26
  • 4
    I didn't downvote, however, one of the possible reasons for downvoting mentioned in the tooltip for the downvote button is "This question is unclear", so, if a user feels that the question is unclear, downvoting is perfectly fine. And even if that one user happens to be mistaken, well, there are 4.1 million others which can upvote. Commented Jul 6, 2015 at 12:38
  • 1
    What's unclear about this question? Commented Jul 6, 2015 at 13:48
  • 1
    Gena, I liked your use of tap, so modified my code for Array#difference (at link given in my answer) to use tap in place of cpy = dup, then cpy as the last line. I like the sound of the method, so much so that sometimes I write obj.tap{}.tap{}.tap { |x| ... }. Commented Jul 7, 2015 at 7:17

3 Answers 3

2

What's so "hacky" about your solution?

This is also what first came to my mind (started hacking away before reading the entire question :P)

class Array
  def except(elem)
    tmp = self.dup
    tmp.delete_at(self.index(elem))
    tmp
  end
end

Or you can use the power of delete_if (including count I stole from @mudasobwa 's answer:

class Array
  def except(elem, count = 1)
    tmp = self.dup
    memo = 0
    tmp.delete_if { |e| elem == e && (memo += 1) <= count }
  end
end

Or you can slice your array to bits:

class Array
  def except(elem)
    index = self.index(elem)
    self.slice(0, index) + self.slice(index + 1, self.length)
  end
end
Sign up to request clarification or add additional context in comments.

5 Comments

Upvoted for slice (which I would rewrite as self[0...index] + self[index+1..-1]).
you can replace your slice calls with: reject.with_index { |_, i| i == index }
This would be the same as the delete_if solution.
@Manuel Thanks, it seems good. Look my own solution under UPD in question
Ah, very interesting! Didn't know about tap! However, I actually prefer your first version because it's much more readable. Technically it's the same solution.
1
class Array
  def except what, count = 1
    memo = 0
    map do |e| 
      what == e && (memo += 1) <= count ? nil : e
    end.compact
  end
end

The above will remove count occurences of what:

a.except(1, 2) 
#⇒ [0,0,0,0,1,0]

1 Comment

Cool solution, but look out if your array looks like this: [0, nil, 0, 0, nil, 0, nil, 0]. But then, who has nils in his arrays anyways...
1

You could write:

a.difference [1]
  #=> [0,0,0,1,0,1,0] 

a #=> [0,1,0,0,1,0,1,0]

where Array#difference is defined in my answer here.

2 Comments

It is the same as that of my solution under UPD in question, cool, thanks
When the argument of difference is an array containing a single element, yes.

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.