I believe this code is the best:
defmodule Example do
# a function head declaring defaults
def sample(list, data \\ [])
# in case where empty list is passed as an input
def sample([], []), do: []
# data here is finished element of output list
# due to prepending the data is reversed
# which is fixed by `:lists.reverse/1`
# we need to wrap the output element into one element list
# otherwise our data is improperly concatenated
# i.e. instead of list of lists we have just a list
# for example: [:a, 1, {4, 5}, :a, 1, {5, 4}, :a, …]
def sample([], data), do: [:lists.reverse(data)]
# when heads i.e. elements of first list ends
# all we need to do is to return an empty list
# to make concatenation work
def sample([[] | _tail], _data), do: []
# collecting data and recursion part
def sample([[sub_head | head] | tail], data) do
# using ++ operator we concatenates two sides each returning a list of lists
# left side returns an output result for first element (nested recursion)
# right side returns an output result for rest elements (tail recursion)
# collecting data is really simple
# we are prepending a first element of head into data
# until we reach end of nested levels
sample(tail, [sub_head | data]) ++ sample([head | tail], data)
end
end
Example.sample([[:a, :b], [1, 2, 3], [{4, 5}, {5, 6}]])
This code should be fastest and work as long as the input
is list contains 1 or more lists.
The other solutions are limited to 3-element
list and one of them have also other problem which was already mentioned by its author:
In my case all of below calls work:
# empty list
Example.sample([])
# one element list with no sub elements
Example.sample([[]])
# one element list
Example.sample([[:a, :b]])
# two element list
Example.sample([[:a, :b], [1, 2, 3]])
# three element list (author's example input)
Example.sample([[:a, :b], [1, 2, 3], [{4, 5}, {5, 6}]])
# three element list with lists as sub elements instead tuple
Example.sample([[:a, :b], [1, 2, 3], [[4, 5], [5, 6]]])
# four element list
Example.sample([[:a, :b], [1, 2, 3], [{4, 5}, {5, 6}], ["abc", "def"]])
Note: If one of root list is empty, for example:
[[:a, :b], [1, 2, 3], [], [{4, 5}, {5, 6}]]
then then my example would properly return empty list. If you want to simply skit empty elements use this code:
def sample([head | [[] | tail]], data), do: sample([head | tail], data)
right before “collecting data and recursion part” comment.
However the above does not work if empty list is a first element. To fix that you need an extra helper function to avoid conflicts in pattern-matching, for example:
defmodule Example do
# if empty list is a first element
def before_sample([[] | tail]), do: before_sample(tail)
# in any other case
def before_sample(list), do: list
# definition of sample function goes here…
end
input = [[], [:a, :b], [1, 2, 3], [{4, 5}, {5, 6}]]
input
|> Example.before_sample()
|> Example.sample()