Я пойду с другим подходом, пытаясь пройти через оба списка, используя хвостовую рекурсию.
Чтобы использовать этот подход, мы должны гарантировать, что оба списка a
и b
будут упорядочены по полям, которые позволяют нам сопоставить, в данном случае school
и class
.
Это необходимо, потому что во время хвостовой рекурсии мы будем сопоставлять списки на лету, и обязательно будет гарантировать, что если мы оставляем несопоставленный элемент a
, невозможно найти совпадение b
позже
# With this both lists will be ordered ascendently by school and class fields.
ordered_a = Enum.sort(a, &((&1["school"] < &2["school"]) || (&1["class"] <= &2["class"] )))
ordered_b = Enum.sort(b, &((&1["school"] < &2["school"]) || (&1["class"] <= &2["class"] )))
При этом оба списка будут упорядочены по возрастанию school
и классу fields
.
Пойдемте с трудной частью. Теперь нам нужно подумать о прохождении двух упорядоченных списков. Рекурсия будет выполнена через функцию match_lists
.
У нас может быть 6 возможных комбинаций заголовков:
- [MATCH] Поля
school
и class
в Head
обоих списков совпадают, поэтому они совпадают. В этом случае мы создаем новый элемент и добавляем его в аккумулятор. При следующем вызове мы просто передаем хвост обоих списков.
- [UNMATCHED B]
Head
элемент a
впереди Head
элемент b
, это поле school
(или поле class
, если school
то же самое) имеет большее значение , Это означает, что для текущего Head
элемента списка b
нет совпадений, так как список a
уже впереди. Таким образом, непревзойденный элемент b
будет построен и добавлен в аккумулятор. При следующем вызове мы только что прошли хвост b
, но полный a list.
- [UNMATCHED A] То же, что в пункте 2, но относительно списка
a
. Элемент Head
списка b
опережает элемент Head
списка a
. Это означает, что для элемента Head
в a
нет соответствия, поскольку Head
в b
уже впереди. Непревзойденный элемент a
будет построен и добавлен в аккумулятор.
- [UNMATCHED B] Список
a
пуст. Непревзойденный B будет сгенерирован с Head
из b
и добавлен в аккумулятор.
- [UNMATCHED A] Список
b
пуст. Непревзойденный A будет сгенерирован с Head
из a
и добавлен в аккумулятор.
- [END] Оба списка пусты. Рекурсия закончилась, и аккумулятор будет возвращен.
def match_lists(a, b, acc \\ [] )
# Case: Element in both lists
def match_lists(
[%{"school" => school, "class" => class, "student" => student} | rest_a],
[%{"school" => school, "class" => class, "choice" => choice} | rest_b],
acc
) do
element = build(school, class, student, [choice], true)
match_lists(rest_a, rest_b, [element | acc])
end
# Case: Element only in list B case. So it is a B case
def match_lists(
[%{"school" => school_a, "class" => class_a} | _] = a,
[%{"school" => school_b, "class" => class_b, "choice" => choice} | rest_b],
acc
)
when school_a > school_b or class_a > class_b do
element = build(school_b, class_b, nil, [choice], "only_list_b")
match_lists(a, rest_b, [element | acc])
end
# Case: No more elementes in A. So It is a B case
def match_lists([], [%{"school" => school, "class" => class, "choice" => choice} | rest_b], acc) do
element = build(school, class, nil, [choice], "only_list_b")
match_lists([], rest_b, [element | acc])
end
# Case: Element only in list A
def match_lists(
[%{"school" => school_a, "class" => class_a, "student" => student} | rest_a],
[%{"school" => school_b, "class" => class_b} | _] = b,
acc
)
when school_b > school_a or class_b > class_a do
element = build(school_a, class_a, student, [], "only_list_a")
match_lists(rest_a, b, [element | acc])
end
# Case: No more elementes in B. So It is an uncommon A case
def match_lists([%{"school" => school, "class" => class, "student" => student} | rest_a], [], acc) do
element = build(school, class, student, [], "only_list_a")
match_lists(rest_a, [], [element | acc])
end
def match_lists([], [], acc) do
acc
end
defp build(school, class, student, choices, is_common) do
%{
"school" => school,
"class" => class,
"student" => student,
"choices" => choices,
"is_common" => is_common
}
end
iex(1)> match_lists(ordered_a, ordered_b)
[
%{
"choices" => [],
"class" => 6,
"is_common" => "only_list_a",
"school" => "c",
"student" => "jane doe2"
},
%{
"choices" => [],
"class" => 9,
"is_common" => "only_list_a",
"school" => "b",
"student" => "jane1 doe"
},
%{
"choices" => ["maths"],
"class" => 6,
"is_common" => "only_list_b",
"school" => "b",
"student" => nil
},
%{
"choices" => ["science"],
"class" => 9,
"is_common" => "only_list_b",
"school" => "a",
"student" => nil
},
%{
"choices" => ["arts"],
"class" => 1,
"is_common" => true,
"school" => "a",
"student" => "jane doe"
}
]
Надеюсь, это поможет.