When i use List.flatten() it would flatten a deep list with every nested [].
iex> list = [[],["ant",["hello","hi",[[[]]]], "bat"], ["cat", "dog"]]
[[], ["ant", ["hello", "hi", [[[]]]], "bat"], ["cat", "dog"]]
iex> List.flatten(list)
["ant", "hello", "hi", "bat", "cat", "dog"]
As expected.
But when i do Stream.flat_map()
iex(6)> list = [[],["ant",["hello","hi",[[[]]]], "bat"], ["cat", "dog"]]
[[], ["ant", ["hello", "hi", [[[]]]], "bat"], ["cat", "dog"]]
iex(7)> list
[[], ["ant", ["hello", "hi", [[[]]]], "bat"], ["cat", "dog"]]
iex(8)> |> Stream.flat_map(& &1)
#Function<60.124013645/2 in Stream.transform/3>
iex(9)> |> Enum.to_list()
["ant", ["hello", "hi", [[[]]]], "bat", "cat", "dog"]
It flattens only the first level of the list. It’s a flatten() with depth == 1.
And that was not what i expected.
I needed a deep_flatten() for Stream, so i’ve made it like this:
defmodule ExTelnet.StreamDeepFlatten do
def deep_flatten(enumerables) do
deep_flat_map(enumerables, & &1)
end
def deep_flatten(first, second) do
deep_flat_map([first, second], & &1)
end
def deep_flat_map(enum, mapper) when is_function(mapper, 1) do
Stream.transform(enum, nil, fn val, nil ->
case val do
val when is_list(val) -> {deep_flat_map(val, mapper), nil}
val -> {[mapper.(val)], nil}
end
end)
end
end
Works as expected for me:
iex(2)> list = [[],["ant",["hello","hi",[[[]]]], "bat"], ["cat", "dog"]]
iex(3)> list
iex(4)> |> Stream.map(&IO.inspect(&1))
iex(5)> |> ExTelnet.StreamDeepFlatten.deep_flatten()
iex(6)> |> Stream.map(&IO.inspect(&1))
iex(7)> |> Stream.map(&("seen " <> &1))
iex(8)> |> Stream.map(&IO.inspect(&1))
iex(9)> |> Enum.to_list()
[]
["ant", ["hello", "hi", [[[]]]], "bat"]
"ant"
"seen ant"
"hello"
"seen hello"
"hi"
"seen hi"
"bat"
"seen bat"
["cat", "dog"]
"cat"
"seen cat"
"dog"
"seen dog"
["seen ant", "seen hello", "seen hi", "seen bat", "seen cat", "seen dog"]
So what i’m questioning myself now is: “Am I reinvening the wheel? Maybe there is some better simpler method and I just don’t see it?”