Skip to content

Commit b898f09

Browse files
author
José Valim
committed
Support zipping of any collection, closes #7219
1 parent e89139b commit b898f09

File tree

4 files changed

+20
-15
lines changed

4 files changed

+20
-15
lines changed

lib/elixir/lib/enum.ex

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2718,10 +2718,10 @@ defmodule Enum do
27182718
end
27192719

27202720
@doc """
2721-
Zips corresponding elements from a list of enumerables
2721+
Zips corresponding elements from a finite collection of enumerables
27222722
into one list of tuples.
27232723
2724-
The zipping finishes as soon as any enumerable in the given list completes.
2724+
The zipping finishes as soon as any enumerable in the given collection completes.
27252725
27262726
## Examples
27272727
@@ -2733,10 +2733,11 @@ defmodule Enum do
27332733
27342734
"""
27352735
@spec zip([t]) :: t
2736+
@spec zip(t) :: t
27362737

27372738
def zip([]), do: []
27382739

2739-
def zip(enumerables) when is_list(enumerables) do
2740+
def zip(enumerables) do
27402741
Stream.zip(enumerables).({:cont, []}, &{:cont, [&1 | &2]})
27412742
|> elem(1)
27422743
|> :lists.reverse()

lib/elixir/lib/stream.ex

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,10 +1091,10 @@ defmodule Stream do
10911091
def zip(left, right), do: zip([left, right])
10921092

10931093
@doc """
1094-
Zips corresponding elements from a list of enumerables
1094+
Zips corresponding elements from a finite collection of enumerables
10951095
into one stream of tuples.
10961096
1097-
The zipping finishes as soon as any enumerable in the given list completes.
1097+
The zipping finishes as soon as any enumerable in the given collection completes.
10981098
10991099
## Examples
11001100
@@ -1105,15 +1105,20 @@ defmodule Stream do
11051105
11061106
"""
11071107
@spec zip([Enumerable.t()]) :: Enumerable.t()
1108-
def zip(enumerables) when is_list(enumerables) do
1108+
@spec zip(Enumerable.t()) :: Enumerable.t()
1109+
def zip(enumerables) do
1110+
&prepare_zip(enumerables, &1, &2)
1111+
end
1112+
1113+
defp prepare_zip(enumerables, acc, fun) do
11091114
step = &do_zip_step(&1, &2)
11101115

11111116
enum_funs =
11121117
Enum.map(enumerables, fn enum ->
11131118
{&Enumerable.reduce(enum, &1, step), :cont}
11141119
end)
11151120

1116-
&do_zip(enum_funs, &1, &2)
1121+
do_zip(enum_funs, acc, fun)
11171122
end
11181123

11191124
# This implementation of do_zip/3 works for any number of
@@ -1128,6 +1133,10 @@ defmodule Stream do
11281133
{:suspended, acc, &do_zip(zips, &1, fun)}
11291134
end
11301135

1136+
defp do_zip([], {:cont, acc}, _callback) do
1137+
{:done, acc}
1138+
end
1139+
11311140
defp do_zip(zips, {:cont, acc}, callback) do
11321141
try do
11331142
do_zip_next_tuple(zips, acc, callback, [], [])

lib/elixir/test/elixir/enum_test.exs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -878,10 +878,7 @@ defmodule EnumTest do
878878
assert Enum.zip([[1]]) == [{1}]
879879

880880
assert Enum.zip([[], [], [], []]) == []
881-
882-
assert_raise FunctionClauseError, fn ->
883-
Enum.zip(%{})
884-
end
881+
assert Enum.zip(%{}) == []
885882
end
886883
end
887884

lib/elixir/test/elixir/stream_test.exs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,10 +1099,8 @@ defmodule StreamTest do
10991099
assert Stream.zip([concat, cycle]) |> Enum.to_list() ==
11001100
[{1, :a}, {2, :b}, {3, :c}, {4, :a}, {5, :b}, {6, :c}]
11011101

1102-
assert_raise FunctionClauseError, fn ->
1103-
enum_of_enums = Stream.cycle([[1, 2], [:a, :b]])
1104-
Stream.zip(enum_of_enums)
1105-
end
1102+
assert Stream.chunk_every([0, 1, 2, 3], 2) |> Stream.zip() |> Enum.to_list() ==
1103+
[{0, 2}, {1, 3}]
11061104
end
11071105

11081106
test "zip/1 does not leave streams suspended" do

0 commit comments

Comments
 (0)