Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Enumerable#each_step and Iterable#each_step #13610

Merged
merged 8 commits into from
Oct 18, 2023
34 changes: 34 additions & 0 deletions spec/std/enumerable_spec.cr
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,40 @@ describe "Enumerable" do
end
end

describe "each_step" do
it "yields the every 2nd element" do
collection = [] of String
%w[a b c d e f].each_step(2) do |e|
collection << e
end
collection.should eq ["a", "c", "e"]
end

it "accepts an optional offset parameter" do
collection = [] of String
%w[a b c d e f].each_step(2, offset: 1) do |e|
collection << e
end
collection.should eq ["b", "d", "f"]
end

it "gets each_step iterator" do
iter = %w[a b c d e f].each_step(2)
iter.next.should eq("a")
iter.next.should eq("c")
iter.next.should eq("e")
iter.next.should be_a(Iterator::Stop)
end

it "gets each_step iterator with an offset" do
iter = %w[a b c d e f].each_step(2, offset: 1)
iter.next.should eq("b")
iter.next.should eq("d")
iter.next.should eq("f")
iter.next.should be_a(Iterator::Stop)
end
end

describe "each_with_index" do
it "yields the element and the index" do
collection = [] of {String, Int32}
Expand Down
36 changes: 36 additions & 0 deletions src/enumerable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,42 @@ module Enumerable(T)
nil
end

# Iterates over the collection, yielding every *n*th element, starting with the first.
#
# ```
# %w[Alice Bob Charlie David].each_step(2) do |user|
# puts "User: #{user}"
# end
# ```
#
# Prints:
#
# ```text
# User: Alice
# User: Charlie
# ```
#
# Accepts an optional *offset* parameter
#
# ```
# %w[Alice Bob Charlie David].each_step(2, 1) do |user|
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
# puts "User: #{user}"
# end
# ```
#
# Which would print:
#
# ```text
# User: Bob
# User: David
# ```
def each_step(n : Int, *, offset : Int = 0, & : T ->) : Nil
offset_mod = offset % n
each_with_index do |elem, i|
yield elem if i >= offset && i % n == offset_mod
end
end

# Iterates over the collection, yielding both the elements and their index.
#
# ```
Expand Down
14 changes: 14 additions & 0 deletions src/iterable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ module Iterable(T)
each.cons_pair
end

# Same as `each.step(n)`.
#
# See also: `Iterator#step`.
def each_step(n : Int)
each.step(n)
end

# Same as `each.skip(offset).step(n)`.
#
# See also: `Iterator#skip`, `Iterator#step`.
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
def each_step(n : Int, *, offset : Int)
each.skip(offset < 0 ? offset % n : offset).step(n)
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
end

# Same as `each.with_index(offset)`.
#
# See also: `Iterator#with_index(offset)`.
Expand Down
Loading