我有两个列表a和b。
a = [
%{"school" => "a", "class" => 1, "student" => "jane doe"},
%{"school" => "b", "class" => 9, "student" => "jane1 doe"},
%{"school" => "c", "class" => 6, "student" => "jane doe2"}
]
b = [
%{"choice" => "arts", "class" => 1, "school" => "a"},
%{"choice" => "science", "class" => 9, "school" => "a"},
%{"choice" => "maths", "class" => 6, "school" => "b"}
]
我希望能够比较两个列表并生成具有以下结构的项目的列表
desired_result = [
%{
"school" => "a",
"class" => 1,
"student" => "jane doe" or nil (if student exists only in list b but not in a),
"choices" => ["arts"] or [] (if student exists only in list a but not in b),
"is_common" => yes(if the student exists in both lists) OR only list a OR only list b
}
]
我尝试使用Enum.into和Enum.member吗?功能,我已经能够实现我想要的解决方案的60%。
Enum.into(a, [], fn item ->
if Enum.member?(b, %{
"school" => item["school"],
"class" => item["class"]
}) do
%{
"school" => item["school"],
"class" => item["class"],
"student" => item["student"],
"choices" => [],
"is_common" => "yes"
}
else
%{
"school" => item["school"],
"class" => item["class"],
"student" => item["student"],
"choices" => [],
"is_common" => "only list a"
}
end
end)
上述问题是它涵盖了两个列表中的常见情况以及仅在列表a中的情况;但是它不包括仅在列表b中的那些。而且,我也找不到从列表b获得最终结果中选择值的方法(如您所见,我将[choice]的值保留为[])。如何覆盖所有三种情况并在所需的结构中获得包含值的列表?
答案 0 :(得分:1)
让我们先从您现有的产品中得出一个简单的结果。我假设school
+ class
对就是定义的唯一性。
[a, b]
|> Enum.map(fn list ->
Enum.group_by(list, & {&1["class"], &1["school"]})
end)
|> Enum.reduce(
&Map.merge(&1, &2, fn _, [v1], [v2] -> [Map.merge(v1, v2)] end))
|> Enum.map(fn {_, [v]} -> v end)
#⇒ [
# %{"choice" => "arts", "class" => 1, "school" => "a", "student" => "jane doe"},
# %{"choice" => "maths", "class" => 6, "school" => "b"},
# %{"class" => 6, "school" => "c", "student" => "jane doe2"},
# %{"choice" => "science", "class" => 9, "school" => "a"},
# %{"class" => 9, "school" => "b", "student" => "jane1 doe"}
# ]
可以随意运行上述逐个子句以查看所有涉及的转换。
上面的列表通过%{"school" => any(), "class" => any()}
保证列表元素之间的唯一性。现在,只需根据需要迭代并更新元素。
答案 1 :(得分:0)
我将采用另一种方法,尝试使用尾部递归遍历两个列表。
为了使用这种方法,我们需要保证两个列表a
和b
都将通过允许我们进行匹配的字段(在这种情况下为school
和{ {1}}。
这是必需的,因为在尾部递归过程中,我们将即时进行列表之间的匹配,并且必须保证如果我们留下不匹配的class
元素,则不可能找到{{ 1}}之后再比赛
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
函数完成。
我们可以使标题的这6种可能的模式匹配:
fields
的{{1}}和match_lists
字段是相同的,因此它们匹配。在这种情况下,我们将构建新元素并将其添加到累加器中。在下一次通话中,我们只传递了两个列表的结尾。school
的[UNMATCHED B] class
元素位于Head
的{{1}}元素之前,这是Head
字段(或a
字段如果Head
相同)则具有更大的值。这意味着列表b
的当前school
元素没有可用的匹配项,因为列表class
已经在它的前面。因此,将构建不匹配的school
元素并将其添加到累加器中。在下一次通话中,我们只传递了Head
的尾部,但完整了b
a
。列表b
的{{1}}元素位于列表b
的{{1}}元素之前。这意味着a list.
中的a
元素不可用,因为Head
中的b
已经在前面。将构建不匹配的Head
元素并将其添加到累加器中。a
为空。 Head
的{{1}}会生成不匹配的B,并将其添加到累加器中。a
为空。 Head
的{{1}}会产生不匹配的A,并将其添加到累加器中。b
a
希望有帮助。