There is only one problem when inserting many values to a list
i.e. indexes. When you insert said 11
(or "A"
in second example) at index 1
then all next list elements have increased their index by 1 and the same happen for every next insert
operation.
There are 2 possible solutions:
-
The simplest is to order list_two
by index
in desc
mode and simply reduce
it over list_one
by just inserting each element to list
. This way we do not need to worry about indexes as we do that in reverse mode and we do not care what happens with all next element indexes.
-
However there is much faster way. We can use pattern matching when calling same function recursively and increase current index (stored as an extra argument) by 1
only when we insert element.
This way instead of rely on element’s index
, which is changed for every insert operation, we are inserting elements in proper place with “shadow” index
which counts only original elements.
Your code is close to 2nd option, but except @benwilson512 suggestions you can also avoid reversing
your list
.
Here is a code example for both ways:
defmodule Example do
# fast/3 assumes that you have proper order of insert_list
# so there is no sorter function used here
def fast(data_list, insert_list, index \\ 0)
# when data_list is empty all we need to do
# is to add each element from insert_list regardless of index
# look that passed index here may be any as it would not be used in pattern matching anymore
def fast([], [{element, _index} | insert_list], index) do
[element | fast([], insert_list, index)]
end
# this clause is even more simpler
# when we don't have anything to insert all we need
# to do is to return rest of data_list as a tail of result list
def fast(data_list, [], _index), do: data_list
# a "shadow" index matches index in insert_list
# we simply insert the element
# and *DO NOT* change "shadow" index
def fast(data_list, [{element, index} | insert_list], index) do
[element | fast(data_list, insert_list, index)]
end
# otherwise inserting element from data_list
# and recursively call same function
# with new data_list and increased "shadow" index
def fast([element | data_list], insert_list, index) do
[element | fast(data_list, insert_list, index + 1)]
end
def simple(data_list, insert_list) do
insert_list
# ensure desc i.e. reverse index order
|> Enum.sort_by(&elem(&1, 1), :desc)
# each tuple from insert_list is passed to anonymous function below
|> Enum.reduce(data_list, fn {item, index}, acc ->
# in this example acc (accumulator) is our data_list
List.insert_at(acc, index, item)
end)
end
end
list_one = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list_two = [{"A", 1}, {"B", 3}, {"C", 6}]
iex> Example.fast(list_one, list_two)
iex> Example.simple(list_one, list_two)
What’s interesting in those example is that both support multiple elements with same index in insert_list
. However since a simple
solution in fact reverses insert_list
the order of inserted elements is reversed, see:
list_one = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list_two = [{"A1", 1}, {"A2", 1}, {"B1", 3}, {"B2", 3}, {"C1", 6}, {"C2", 6}]
iex> Example.fast(list_one, list_two)
[1, "A1", "A2", 2, 3, "B1", "B2", 4, 5, 6, "C1", "C2", 7, 8, 9, 10]
iex> Example.simple(list_one, list_two)
[1, "A2", "A1", 2, 3, "B2", "B1", 4, 5, 6, "C2", "C1", 7, 8, 9, 10]
I believe those examples are interesting also for a learning purposes.
Helpful resources:
- Enum.reduce/3
- Enum.sort_by/3
- List.insert_at/3
- Patterns and Guards