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?

Gena Shumilkin
  • 713
  • 4
  • 16
  • 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. – Dave Newton Jul 06 '15 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. – Dave Newton Jul 06 '15 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. – Jörg W Mittag Jul 06 '15 at 12:38
  • 1
    What's unclear about this question? – Stefan Jul 06 '15 at 13:48
  • Could the people who put this question on hold please elaborate what's unclear about it? The Question is pretty much spot-on... – mhutter Jul 06 '15 at 13:57
  • Gena, a couple of points about your method `Array#except`: 1) `index`'s argument should be `elem`; 2) `self.` is optional. That is, you write: `dup.tap { |a| a.delete_at(a.index(elem)) }`. – Cary Swoveland Jul 06 '15 at 20:01
  • @CarySwoveland oh, right, thank you – Gena Shumilkin Jul 07 '15 at 04: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| ... }`. – Cary Swoveland Jul 07 '15 at 07:17

3 Answers3

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
mhutter
  • 2,800
  • 22
  • 30
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]
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • 1
    Cool solution, but look out if your array looks like this: `[0, nil, 0, 0, nil, 0, nil, 0]`. But then, who has `nil`s in his arrays anyways... – mhutter Jul 06 '15 at 13:49
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.

Community
  • 1
  • 1
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100