which mentions the Stream.unzip/1 function but I noticed that Stream.unzip/1 does not exist in the std library.
Was it present earlier and was deprecated or was it never added or was it decided not to add it for some reason?
If it makes sense to add this to the std library, happy to send a PR for review.
-
https://github.com/n1lanjan/elixir/commit/f720477f21feebc938ed9effd58bf16fd04e0089
```diff
diff --git a/lib/elixir/lib/stream.ex b/lib/elixir/lib/stream.ex
index 81704f1e3..79622cd95 100644
--- a/lib/elixir/lib/stream.ex
+++ b/lib/elixir/lib/stream.ex
@@ -1383,6 +1383,38 @@ def zip_with(enumerables, zip_fun) do
R.zip_with(enumerables, zip_fun)
end
+ @doc """
+ Opposite of `zip/2`. Lazily splits a stream of two-element tuples into two streams.
+
+ It returns a tuple with two streams. Each stream enumerates the corresponding
+ element of the input tuples.
+
+ Each returned stream enumerates the input independently. Enumerating both
+ streams will traverse the input twice. If your input is a resource or costs
+ to enumerate, consider materializing once with `Enum.unzip/1`.
+
+ This function expects elements to be two-element tuples. Otherwise, it will
+ fail at enumeration time.
+
+ ## Examples
+
+ iex> {left, right} = Stream.unzip([{:a, 1}, {:b, 2}, {:c, 3}])
+ iex> Enum.to_list(left)
+ [:a, :b, :c]
+ iex> Enum.to_list(right)
+ [1, 2, 3]
+
+ """
+ @doc since: "1.19.0"
+ @spec unzip(Enumerable.t({left, right})) :: {Enumerable.t(left), Enumerable.t(right)}
+ when left: term, right: term
+ def unzip(enumerable) do
+ {
+ map(enumerable, fn {left, _right} -> left end),
+ map(enumerable, fn {_left, right} -> right end)
+ }
+ end
+
## Sources
@doc """
diff --git a/lib/elixir/test/elixir/stream_test.exs b/lib/elixir/test/elixir/stream_test.exs
index ed62a1cfa..297e4b729 100644
--- a/lib/elixir/test/elixir/stream_test.exs
+++ b/lib/elixir/test/elixir/stream_test.exs
@@ -1342,6 +1342,53 @@ test "zip_with/2 does not leave streams suspended on halt" do
assert Process.get(:stream_zip_with) == :done
end
+ test "unzip/1 is lazy" do
+ {left, right} = Stream.unzip([{:a, 1}])
+ assert lazy?(left)
+ assert lazy?(right)
+ end
+
+ test "unzip/1 basic" do
+ {left, right} = Stream.unzip([{:a, 1}, {:b, 2}, {:c, 3}])
+ assert Enum.to_list(left) == [:a, :b, :c]
+ assert Enum.to_list(right) == [1, 2, 3]
+ end
+
+ test "unzip/1 enumerates the input independently for each side" do
+ Process.put(:stream_unzip_calls, 0)
+
+ source =
+ Stream.map([{:a, 1}, {:b, 2}], fn tuple ->
+ Process.put(:stream_unzip_calls, Process.get(:stream_unzip_calls) + 1)
+ tuple
+ end)
+
+ {left, right} = Stream.unzip(source)
+ assert Enum.to_list(left) == [:a, :b]
+ assert Enum.to_list(right) == [1, 2]
+ assert Process.get(:stream_unzip_calls) == 4
+ end
+
+ test "unzip/1 roundtrips with zip/2" do
+ concat = Stream.concat(1..3, 4..6)
+ cycle = Stream.cycle([:a, :b, :c])
+ zipped = Stream.zip(concat, cycle)
+
+ {left, right} = Stream.unzip(zipped)
+ assert Enum.to_list(Stream.zip(left, right)) == Enum.to_list(zipped)
+ end
+
+ test "unzip/1 raises on non-tuple elements at enumeration time" do
+ {left, _right} = Stream.unzip([:a, :b, :c])
+ assert_raise FunctionClauseError, fn -> Enum.to_list(left) end
+ end
+
+ test "unzip/1 on empty input" do
+ {left, right} = Stream.unzip([])
+ assert Enum.to_list(left) == []
+ assert Enum.to_list(right) == []
+ end
+
test "zip_with/2 closes on inner error" do
zip_with_fun = &List.to_tuple/1
stream = Stream.into([1, 2, 3], %Pdict{})
```