我是想用两个维度进行分组,然后统计每个类别子类别中每个元素的数量。
data = [
%{brand: "Mercedes", color: "blue"},
%{brand: "Mercedes", color: "blue"},
%{brand: "BMW", color: "blue"},
%{brand: "BMW", color: "blue"},
%{brand: "BMW", color: "blue"},
%{brand: "Lada", color: "blue"},
%{brand: "Mercedes", color: "red"},
%{brand: "Mercedes", color: "red"},
%{brand: "Mercedes", color: "red"},
%{brand: "Mercedes", color: "red"},
%{brand: "BMW", color: "black"}
]
的预期结果。
[
%{"Mercedes": ["blue": 2, "red": 4]},
%{"Lada": ["blue": 1]},
%{"BMW": ["blue": 3, "black": 1]}
]
从结果来看,我有一些接近的东西, 但它并不完全是我想要的。
per_brands = Enum.group_by(data, fn (item) -> [item[:brand]] end)
Enum.map(
per_brands,
fn (cars_per_brand) ->
%{
"#{elem(cars_per_brand, 0)}": (
Enum.map(
Enum.group_by(elem(cars_per_brand, 1), fn (car) -> car[:color] end),
fn (cars_per_brands_per_colors) ->
%{"#{elem(cars_per_brands_per_colors, 0)}": Enum.count(elem(cars_per_brands_per_colors, 1))}
end
))
}
end
)
# Result: (not exactly what I want)
[
%{BMW: [%{black: 1}, %{blue: 3}]},
%{Lada: [%{blue: 1}]},
%{Mercedes: [%{blue: 2}, %{red: 4}]}
]
我相信有一些更聪明更好的方法来处理 reduce/3
但我无法理解。
你可以用reduce这样做。
Enum.reduce(data, %{}, fn %{brand: brand, color: color}, acc ->
colors = Map.get(acc, brand, %{}) |> Map.update(color, 1, &(&1 + 1))
Map.put(acc, brand, colors)
end)
对于每一个品牌颜色对,建立一个地图的地图, 当发现颜色计数时就递增。
结果。
%{
'BMW' => %{'black' => 1, 'blue' => 3},
'Lada' => %{'blue' => 1},
'Mercedes' => %{'blue' => 2, 'red' => 4}
}
我做的事情比我最初的方法要好得多 Map.Reduce
和 Access.key/2
也是。
Enum.reduce(
data,
%{},
fn (item, acc) ->
new_value = if (acc[item[:brand]][item[:color]]) do
# The value already exists
acc[item[:brand]][item[:color]] + 1
else
# The value does not exist, initialize to 1
1
end
acc = put_in(acc, [Access.key(item[:brand], %{}), item[:color]], new_value)
end
)
我觉得@亚当的解决方案更整洁,虽然。