Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,39 @@ defmodule Enum do
do_drop(reverse(enumerable), abs(n)) |> :lists.reverse
end

@doc """
Returns a list of every `nth` item in the enumerable dropped,
starting with the first element.

The first item is always dropped, unless `nth` is 0.

The second argument specifying every `nth` item must be a non-negative
integer, otherwise `FunctionClauseError` will be raised.

## Examples

iex> Enum.drop_every(1..10, 2)
[2, 4, 6, 8, 10]

iex> Enum.drop_every(1..10, 0)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

iex> Enum.drop_every([1, 2, 3], 1)
[]

"""
@spec drop_every(t, non_neg_integer) :: list | no_return
def drop_every(enumerable, nth)

def drop_every(_enumerable, 1), do: []
def drop_every(enumerable, 0), do: to_list(enumerable)
def drop_every([], _nth), do: []

def drop_every(enumerable, nth) when is_integer(nth) and nth > 0 do
{res, _} = reduce(enumerable, {[], :first}, R.drop_every(nth))
:lists.reverse(res)
end

@doc """
Drops items at the beginning of the enumerable while `fun` returns a
truthy value.
Expand Down
30 changes: 30 additions & 0 deletions lib/elixir/lib/stream.ex
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,36 @@ defmodule Stream do
end
end

@doc """
Creates a stream that drops every `nth` item from the enumerable.

The first item is always dropped, unless `nth` is 0.

`nth` must be a non-negative integer, or `FunctionClauseError` will be thrown.

## Examples

iex> stream = Stream.drop_every(1..10, 2)
iex> Enum.to_list(stream)
[2, 4, 6, 8, 10]

iex> stream = Stream.drop_every(1..1000, 1)
iex> Enum.to_list(stream)
[]

iex> stream = Stream.drop_every([1, 2, 3, 4, 5], 0)
iex> Enum.to_list(stream)
[1, 2, 3, 4, 5]

"""
@spec drop_every(Enumerable.t, non_neg_integer) :: Enumerable.t
def drop_every(enum, 0), do: %Stream{enum: enum}
def drop_every([], _nth), do: %Stream{enum: []}

def drop_every(enum, nth) when is_integer(nth) and nth > 0 do
lazy enum, nth, fn(f1) -> R.drop_every(nth, f1) end
end

@doc """
Lazily drops elements of the enumerable while the given
function returns `true`.
Expand Down
12 changes: 12 additions & 0 deletions lib/elixir/lib/stream/reducers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ defmodule Stream.Reducers do
end
end

defmacro drop_every(nth, f \\ nil) do
quote do
fn
entry, acc(h, n, t) when n === :first
when n === unquote(nth) ->
skip(acc(h, 1, t))
entry, acc(h, n, t) ->
next_with_acc(unquote(f), entry, h, n+1, t)
end
end
end

defmacro drop_while(callback, f \\ nil) do
quote do
fn entry, acc(h, bool, t) = orig ->
Expand Down
14 changes: 14 additions & 0 deletions lib/elixir/test/elixir/enum_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,20 @@ defmodule EnumTest do
assert Enum.drop([], 3) == []
end

test "drop every" do
assert Enum.drop_every([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2) == [2, 4, 6, 8, 10]
assert Enum.drop_every([], 2) == []
assert Enum.drop_every([1, 2], 2) == [2]
assert Enum.drop_every([1, 2, 3], 0) == [1, 2, 3]
assert Enum.drop_every(1..3, 1) == []
assert_raise FunctionClauseError, fn ->
Enum.drop_every([1, 2, 3], -1)
end
assert_raise FunctionClauseError, fn ->
Enum.drop_every(1..10, 3.33)
end
end

test "drop while" do
assert Enum.drop_while([1, 2, 3, 4, 3, 2, 1], fn(x) -> x <= 3 end) == [4, 3, 2, 1]
assert Enum.drop_while([1, 2, 3], fn(_) -> false end) == [1, 2, 3]
Expand Down
30 changes: 30 additions & 0 deletions lib/elixir/test/elixir/stream_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,36 @@ defmodule StreamTest do
refute_receive {:stream, 3}
end

test "drop_every/2" do
assert 1..10
|> Stream.drop_every(2)
|> Enum.to_list == [2, 4, 6, 8, 10]

assert 1..10
|> Stream.drop(2)
|> Stream.drop_every(2)
|> Stream.drop(1)
|> Enum.to_list == [6, 8, 10]

assert 1..10
|> Stream.drop_every(0)
|> Enum.to_list == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

assert []
|> Stream.drop_every(10)
|> Enum.to_list == []
end

test "drop_every/2 without non-negative integer" do
assert_raise FunctionClauseError, fn ->
Stream.drop_every(1..10, -1)
end

assert_raise FunctionClauseError, fn ->
Stream.take_every(1..10, 3.33)
end
end

test "drop_while/2" do
stream = Stream.drop_while(1..10, &(&1 <= 5))
assert is_lazy(stream)
Expand Down