category
category

Reputation: 2165

insert at every nth index of a list

Is there a standard Elixir function that can insert an element at every nth index of a list?

With a function call and return like:

iex> List.insert_at_every([1,2,3,4,5], 2, Enum.random(["apple","banana"]))
[1, 2, "apple", 3, 4, "apple", 5]

Upvotes: 1

Views: 624

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

NB the solution proposed by @Dogbert is better by all means, I am posting this for the sake of diversity.


To intersperse the list with a constant value, one might use Enum.chunk_every/2 and Enum.intersperse/2:

iex(1)> [1,2,3,4,5]
...(1)> |> Enum.chunk_every(2)
...(1)> |> Enum.intersperse("banana")
...(1)> |> List.flatten
#⇒ [1, 2, "banana", 3, 4, "banana", 5]

This won’t work if you want to use a function to retrieve an element to intersperse on each iteration. In that case you are to implement the interspersion yourself:

iex(2)> [1,2,3,4,5]
...(2)> |> Enum.chunk_every(2)
...(2)> |> Enum.map(& &1 ++ [Enum.random(~w|banana apple|)])
...(2)> |> List.flatten
#⇒ [1, 2, "banana", 3, 4, "apple", 5, "apple"]

The result above always contains a redundant trailing element, it should be shaved off afterwards:

iex(3) > with [_ | result] <- :lists.reverse([1, 2, "banana", 3, 4, "apple", 5, "apple"]) do
...(3)>     :lists.reverse(result)
...(3)> end
#⇒ [1, 2, "banana", 3, 4, "banana", 5]

Upvotes: 3

Dogbert
Dogbert

Reputation: 222238

There isn't a built in function for this as far as I know, but this can be done using Enum.with_index and Enum.flat_map. When the remainder of the index and the second argument is the second argument minus 1, we insert the element + the callback into the resulting list, and just the element otherwise. I think it makes more sense that passing 1 as the every argument results in your example list instead of 2. You can simply change every with every - 1 if you want though.

defmodule A do
  def insert_at_every(list, every, fun) do
    list
    |> Enum.with_index
    |> Enum.flat_map(fn {x, i} ->
      if rem(i, every) == every - 1 do
        [x, fun.()]
      else
        [x]
      end
    end)
  end
end

IO.inspect A.insert_at_every([1, 2, 3, 4, 5], 1, fn -> Enum.random(["apple", "banana"]) end)
IO.inspect A.insert_at_every([1, 2, 3, 4, 5], 2, fn -> Enum.random(["apple", "banana"]) end)
IO.inspect A.insert_at_every([1, 2, 3, 4, 5], 3, fn -> Enum.random(["apple", "banana"]) end)

Output:

[1, "apple", 2, "apple", 3, "banana", 4, "banana", 5, "apple"]
[1, 2, "apple", 3, 4, "apple", 5]
[1, 2, 3, "apple", 4, 5]

Upvotes: 2

Related Questions