Feature #14114
openAdd #step for Array, Enumerable, Enumerator
Description
This must have been discussed before,
please reassigned and close this one.
I want to propose an extension to the ruby core api:
- add #step(n) for Enumerable, Enumerator
- coerce the step() api across all the api (see Numeric#step(by:n, to:m))
- extend to: #step((by:n, to:m, offset:k)
We have Range#step,
but #step is not yet defined for Array, Enumerable, Enumerator
I believe, the semantics of #step, as defined in Range
applies like well for Enumerable and Enumerator
It should return every n'th element of the collection.
As of 2.4 #step is not yet defined for Enumerable/Enumerator.
But the semantics of this method is already well defined for Range.
That same semantics should also apply for Enumerable#step(n)
aka: pick every n'th element.
Examples:
('a'..'z').step(2)
=> #<Enumerator: ...>
('a'..'z').to_a.step(2)
NoMethodError: undefined method `step'
('a'..'z').to_enum.step(2)
NoMethodError: undefined method `step'
Updated by shevegen (Robert A. Heiler) about 7 years ago
I think you filed this in the wrong subsection (Bug rather than Feature).
To the topic - I am not sure if .step() makes a lot of sense to Array.
I have had a look at your suggestion but you do not give any example
for Array#step, for example, so it is difficult to know what exactly
you want to see.
Consider this Array:
array = %w( a b c 1 2 3 4 )
What would .step(2) return on this Array?
Updated by Hanmac (Hans Mackowiak) about 7 years ago
you might use each_slice
for this:
module Enumerable
def step(n)
each_slice(n).map(&:first)
end
end
Updated by shan (Shannon Skipper) over 6 years ago
Here's a pure Ruby implementation of Enumerable#step, just for fun:
module Enumerable
def step step = 1
raise TypeError, 'no implicit conversion of Object into Integer' unless step.respond_to? :to_int
step_count = step.to_int
raise TypeError, "can't convert #{step.class} to Integer (#{step.class}#to_int gives #{step_count.class})" unless step_count.instance_of? Integer
raise TypeError, "step can't be 0" if step_count.zero?
raise TypeError, "step can't be negative" if step_count.negative?
if block_given?
each_slice step_count do |this_step, *_rest|
yield this_step
end
self
else
lazy_size = size.fdiv(step_count).ceil if size
Enumerator.new lazy_size do |yielder|
each_slice step_count do |this_step, *_rest|
yielder << this_step
end
end
end
end
end
A gist of the same, with some specs mostly borrowed from Range#step: https://gist.github.com/havenwood/501b7a7e57f512ec95cfcb7f9a44f0d0
Updated by jeremyevans0 (Jeremy Evans) over 5 years ago
- Tracker changed from Bug to Feature
- Backport deleted (
2.3: UNKNOWN, 2.4: UNKNOWN)