Filling the tail of a list upto a length with default values?

Is there a more idiomatic way to fill the tail of a list with default values?

That is, given a list, return a list that is at least n elements long, with a default value inserted if needed:

x = [1, 2, 3]
fill(x, 5, :default) # => [1, 2, 3, :default, :default]

This is what I came up with, you could also do it with zip, if the first arg was the correct length filled with default values, but that seems needlessly “allocate-y”. Also you could do some recursive function.

# This seems the most simple to understand, but could be expensive if the lists are big?
fill_concat = fn list, len, default ->
  # Stream.repeatedly (or cycle) probably best if count is large enough
  # to make [:default, :default ...] problematic if just writing this
  # directly in another function.
  |> Enum.concat(Stream.repeatedly(fn -> default end) |> Enum.take(len))
  |> Enum.take(len)

fill_stream =
  fn list, len, default ->
    # *probably* faster, at least when the size is under the cost of creating a stream etc.
    Stream.unfold({list, len}, fn
      {_, 0} -> nil
      {[x | xs], count} -> {x, {xs, count - 1}}
      {[], count} -> {default, {[], count - 1}}
    |> Enum.to_list()

x = [1, 2]

fill_concat.(x, 5, :concat)
|> IO.inspect()

fill_stream.(x, 5, :stream)
|> IO.inspect()

Maybe this is the way to do it, but it feels a bit over done?

|> Stream.concat(Stream.repeatedly(fn -> default end))
|> Enum.take(len)

This should be pretty sane as it’ll iterate list only once, touching Stream.repeatedly(fn -> default end) only if needed.

1 Like

Doh! Somehow I missed Stream.concat!

Edit: Is there not a way to select “solutions” now?

I changed your post to “Questions”, now you can select a solution. You originally marked your post as “Chat” and there are no marked solutions there.

1 Like