[Proposal] Stream.traverse/1

45 views
Skip to first unread message

Clark Kampfe

unread,
Jul 22, 2017, 1:40:47 AM7/22/17
to elixir-lang-core
In Elixir and in other languages I've had the need to traverse arbitrarily nested, complex, tree-like structures, maybe lookup certain things about them, transform, filter, and generally just pull them apart.

Things like processing JSON blobs or other data with with lots of nesting and poor definition originating from systems outside of my control are pretty common where I work.

In the past I've done this in Elixir with pattern matching and recursion, but this can be awkward to model for large structures, and feels slightly lower-level than using a function in Enum or Stream that composes better with other functions in those modules.

I looked in Elixir to see if there was a function to lazily walk a nested Enum depth-first, but couldn't find one, so I'm proposing `Stream.traverse/1` (`walk/1` is a good name, too). A few examples of what this might look like (newlines added in results for readability):

iex> Stream.traverse([:a, [:b, :c, :d, [:e, :f]]]) |> Enum.to_list
[:a,
 
[:b, :c, :d, [:e, :f]],
 
:b,
 
:c,
 
:d,
 
[:e, :f],
 
:e,
 
:f]


iex> Stream.traverse(%{a: %{b: :c, d: %{e: :f}}})
...> |> Stream.map(fn({k, _v}) -> k end)
...> |> Enum.to_list
[:a, :b, :d, :e]

The function emits elements based on the kind of Enum being walked, so lists emit single elements and maps emit `{key, value}` tuples.

The function's behavior should be such that if it is an Enum, it is "walkable". ie, any combination of lists and maps should just work, regardless of their nesting. Lists can nest in maps, maps can nest in lists, etc.

I went ahead and created a patch here, with more examples: https://github.com/elixir-lang/elixir/pull/6369

Thanks!


Burdock

unread,
Jul 23, 2017, 7:48:58 PM7/23/17
to elixir-lang-core
I would suggest using the most common nomenclature for this function: flatten and deep flatten.

Currently, the closest thing we have to flatten is:
flat_map( &(&1) )

I would personally prefer the syntax: 
flatten(enums) # Just like flat_map( &(&1) )
flatten
(enums, deep: true) # Just like the proposed Stream.traverse

Reply all
Reply to author
Forward
0 new messages