functional-programming - 在 Elixir 中展平嵌套地图列表
问题描述
我正在尝试展平嵌套地图列表,以便输出是地图列表,然后可以将其插入数据库表中。嵌套地图可以包含地图列表。一般来说,嵌套地图的最小示例是这样的:
%{
a: "a",
b: "b",
c: [
%{
d: "d1",
e: [%{f: "f1", g: "g1"}],
h: "h1",
i: "i1"
},
%{
d: "d2",
e: [%{f: "f2", g: "g2"}],
h: "h2",
i: "i2"
}
]
}
我要寻找的输出是:
[
%{f: "f1", g: "g1", d: "d1", h: "h1", i: "i1", b: "b", a: "a"},
%{f: "f2", g: "g2", d: "d2", h: "h2", i: "i2", b: "b", a: "a"}
]
列表的长度等于“终端”映射的数量(即f
本例中的键)。您还会注意到嵌套发生的位置,c
并且e
,这些键不是必需的,因此被删除。
我试图递归映射键,但我遇到的问题是输出始终是父映射中键的长度。
任何有关如何解决此问题的帮助或想法将不胜感激。
谢谢!
解决方案
因为当我们遇到一个列表作为一个值时,我们需要从字面上分叉遍历这个级别,所以我们最好的朋友是Task.async_stream/3
. 一旦我们要懒惰地执行它,所有的内部操作也是懒惰的,直到我们需要终止结果以从flatten/1
(with Enum.to_list/1
.)
defmodule Flat do
@spec flatten(map()) :: [map()]
def flatten(input),
do: input |> do_flatten([%{}]) |> Enum.to_list()
@doc "here we fork it in parallel and collect results"
defp do_flatten([%{}|_] = input, acc) do
input
|> Task.async_stream(&do_flatten(&1, acc))
|> Stream.flat_map(&elem(&1, 1))
end
@doc """
add `{key, value}` pairs to each list
in currently accumulated result
"""
defp do_flatten(%{} = input, acc) do
Stream.flat_map(acc, fn list ->
Enum.reduce(input, [list], &do_flatten(&1, &2))
end)
end
@doc "enumerable as value → go for it"
defp do_flatten({_k, v}, acc) when is_list(v) or is_map(v),
do: do_flatten(v, acc)
@doc "the leaf, add to all lists in the accumulator"
defp do_flatten({k, v}, acc),
do: Stream.map(acc, &Map.put(&1, k, v))
end
input = %{
a: "a", b: "b",
c: [
%{d: "d1", e: [%{f: "f1", g: "g1"}], h: "h1", i: "i1"},
%{d: "d2", e: [%{f: "f2", g: "g2"}], h: "h2", i: "i2"}]
}
Flat.flatten()
#⇒ [
# %{a: "a", b: "b", d: "d1", f: "f1", g: "g1", h: "h1", i: "i1"},
# %{a: "a", b: "b", d: "d2", f: "f2", g: "g2", h: "h2", i: "i2"}
# ]
这是一篇博文,以“狼、山羊、卷心菜”之谜为例详细解释了这种技术。
推荐阅读
- excel - VBA excel:从多个文件中复制特定的单元格和列
- java - How exactly the Firebase realtime database usually charge?
- google-bigquery - 在 BigQuery CLI 中按作业状态过滤
- java - 将 ResultSet 转换为泛型类,其中类构造函数通过工厂定义
- javascript - reCaptchs 从数据回调函数中访问包含 DOM 元素的局部变量
- php - sqlsrv_connect ConnectionInfo 不接受 ConnectRetryCount 选项
- java - Android studio - 将 json 列表添加到数组列表中
- c++ - 我的 If Else 语句无法在向量中提供最大值(C++)
- python - 如何使用 tf.train 训练使用 tf.Keras 模型创建的模型?
- dart - 重新格式化 ListTile UI Flutter