2

I want to build a custom method Array#drop_every(n) (I know it's monkey patching, I am doing this for a homework), which returns a new array omitting every nth element:

[4, 8, 15, 16, 23, 42].drop_every(2) # [4, 15, 23]

I want to implement it with Array#delete_if, but by referring to the index and not to the element itself, (similar to each_index) something like this:

def drop_every(step)
  self.delete_if { |index| index % step == 0 }
end

How do I do this? I don't insist on using delete_if, I also looked at drop_while and reject, other suggestions are welcome.

1
  • 1
    make sure to see the updated answer as one before did not work for step > 2 Commented Oct 11, 2013 at 12:37

6 Answers 6

3

You can use with_index method that returns enumerator, filter your collection and then get rid of the indexes.

class Array
  def drop_every(step)
    self.each.with_index.select { |_, index| index % step == 0 }.map(&:first)
  end
end

[4, 8, 15, 16, 23, 42].drop_every(2) # => [4, 15, 23]
Sign up to request clarification or add additional context in comments.

Comments

2
def drop_every(step)
  reject.with_index { |x,i| (i+1) % step == 0 }
end

[4, 8, 15, 16, 23, 42].reject.with_index{|x,i| (i+1) % 2 ==  0}
# => [4, 15, 23]

[4, 8, 15, 16, 23, 42].reject.with_index{|x,i| (i+1) % 3 ==  0}
# => [4, 8, 16, 23] 

2 Comments

I like the short conciseness of your solution, but the method returns exactly the opposite of what I want - it returns every nth element and I want it to omit every nth element.
@AlexPopov changed by solution as finally got what you meant...the previous one did not work for steps more than 2
2

You could use the values_at method to selectively filter out indices which you want.

class Array
  def drop_every(step)
    self.values_at(*(0...self.size).find_all{ |x| (x+1) % step != 0 })
  end    
end

The answer was accepted while I was typing it. I will post it anyways.

3 Comments

use self.size -1 else it will return nil for last element in some cases.
@tihom I used ...(half open interval)
nice..did not know that syntax...my bad
1
def drop_every step
  delete_if.with_index(1){|_, i| i.%(step).zero?}
end

Comments

1
class Array
  def drop_every(step)
    self.each_slice(step).flat_map{|slice| slice[0..-2]}
  end
end

p [4, 8, 15, 16, 23, 42].drop_every(2) #=> [4, 15, 23]

Comments

1

I'd extend the Enumerable mixin instead:

module Enumerable
  def drop_every(step)
    return to_enum(:drop_every, step) unless block_given?
    each.with_index(1) do |o, i|
      yield o unless i % step == 0
    end
  end
end

(1..10).drop_every(3) { |a| p a }
# outputs below
1
2
4
5
7
8
10

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.