Add method to Ruby's Enumerable module that produces every nth element

The task is to enhance the Enumerable module with a method nth().

The method shall create a new list, which contains every nth-element of the object, when it is invoked.

For example: [2, 4, 6, 8, 10, 12].nth(2) shall return [4, 8, 12]

Here's my implementation:

module Enumerable
def nth(n)
i = 0
ordinal = 1
relevant_iteration = n * ordinal

self.select do | num |
i += 1

if i == relevant_iteration
ordinal += 1
relevant_iteration = ordinal * n

num
end
end
end
end

print [2, 4, 6, 8, 10, 12].nth(2) # => [4, 8, 12]
print "\n"
print (4..12).nth(3)  # => [6, 9, 12]


It works as expected and it has passed the unit-tests.

But: My solution isn't elegant. At least that's my own impression.

• Could my code be improved in regard to more compactness?
• Is my code readable and understandable?
• What could be improved?
• This isn't exactly an answer because I feel like you're looking for something more complicated/interesting, but you could also do this with def nth(n); step(n).to_a; end – thesecretmaster Aug 3 at 18:39

nth slicing

Perhaps you could use a slice with map instead.

module Enumerable
def nth(n)
self.each_slice(n).select{|c| c.length == n}.map(&:last)
end
end


unit tests

print [2, 4, 6, 8, 10, 12].nth(3) # => [6, 12]
print "\n"
print [2, 4, 6, 8, 10].nth(3) # => [6]
print "\n"
print [2, 4, 6, 8].nth(3) # => [6]
print "\n"
print [2, 4, 6].nth(3) # => [6]
print "\n"
print [2, 4].nth(3) # => []
print "\n"


Rubocop Report

Skipping an iteration should be done like this..

 next unless i == relevant_iteration

ordinal += 1
relevant_iteration = ordinal * n


rather than..

if i == relevant_iteration
ordinal += 1
relevant_iteration = ordinal * n


General Guidelines:

• use 2 spaces for indentation
• frozen string
• n as parameter name: min length should be 3 -> takeIndex instead?

Refactored

# frozen_string_literal: true

module Enumerable
def nth(takeIndex)
i = 0
ordinal = 1
relevant_iteration = takeIndex * ordinal

self.select do |num|
i += 1

next unless i == relevant_iteration

ordinal += 1
relevant_iteration = ordinal * takeIndex

num
end
end
end

• You seem to have experience with a broad variety of programming languages. I really appreciate your participation here. – πάντα ῥεῖ Jul 28 at 9:28
• @πάνταῥεῖ Thanks for this welcoming feedback. – dfhwze Jul 28 at 9:30
• I dunno about renaming n to skip. In my mind, I would assume that skip=1 means "every second element". – 200_success Jul 29 at 5:37
• @200_success you are right, skip is not the best name. I would personally accept n. But Ruby guidelines stipulate longer variable names. – dfhwze Jul 29 at 14:26