3
votes

The Elixir zip/2 function zips two lists, stopping when the end of the shorter list is reached. I have been attempting to do the opposite -- essentially duplicating items from the shorter list until the end of the longest list is reached.

Here's what I have:

def zipReduce(m, n) when is_list(m) and is_list(n) do
  { long, short } = if length(m) > length(n)
    do { m, n }
    else { n, m }
  end

  Enum.reduce(0..Integer.mod(length(long), length(short)), [], fn i, acc ->
    long
    |> Enum.slice(length(short) * i, length(short))
    |> Enum.zip(short)
    |> Enum.into(acc)
  end)
end

It does work, but I am not a fan of all those calls to length/1 because each one requires a full list traversal and it's inside a potentially quite large reduction. I could cache the lengths inside the tuple along with the short and long lists, but that feels very imperative (although I'll do it for the performance gain if necessary).

Is there a way of doing this that I haven't thought of? Possibly an entirely different approach?

1

1 Answers

10
votes

Do something like this:

Enum.zip(Stream.cycle(short), long)

e.g

iex(1)> Enum.zip(Stream.cycle([1,2,3,4]), [:a, :b, :c, :d, :e, :f, :g, :h])
[{1, :a}, {2, :b}, {3, :c}, {4, :d}, {1, :e}, {2, :f}, {3, :g}, {4, :h}]

This will cycle through elements on shorter list as much as there's data in the longer list.