如何同时映射和group_by?
举个例子,假设我有一个对的
集合,分别是 {第一,第二}
。使用
As an example, let's say I have an enumerable collection
of pairs {first, second}
. Grouping these pairs using
Enum.group_by(collection, fn {first, second} -> first end)
会生成 Map
的键,该键的键由传递的键确定。匿名功能。它的值是对的集合。
但是,我希望其值包含该对的 second
元素。
will result in a Map
whose keys are determined by the passed anonymous function. Its values are collections of pairs.
However, I would like its values to contain the pair's second
elements instead.
一般而言,给定一个可枚举的对象,我想分组提供键提取器和一个值映射器,以便我可以确定将什么放入结果地图
的值。即,我想要类似的东西
In general, given an enumerable, I would like to group providing both a key extractor and a value mapper, so that I can determine what gets put into the resulting Map
's values. I.e., I would like something like
map_group_by(
collection,
fn {_first, second} -> second end,
fn {first, _second} -> first end
)
其中集合
的值在被分组之前已被映射,但是键映射器仍在原始元素上运行。
where collection
's values are mapped before being grouped, yet where the key mapper still operates on the original elements.
标准库中是否有这样的功能?如果没有,实现此目标的最惯用的方法是什么?
我知道我可以做类似的事情
I know I could do something like
Enum.reduce(
collection,
%{},
fn({key, value}, acc) -> Dict.update(acc, key, [value], &([value | &1])) end
)
但这似乎很笨拙,并且会抢先创建 [value]
列表(实际上是真的吗?)。有没有一种既简洁又有效的更好方法呢?
but this seems clunky and creates [value]
lists preemptively (is that actually true?). Is there a better way that is both concise and efficient?
自Elixir 1.3开始,现在有了 Enum.group_by / 3
需要一个 mapper_fun
参数,它可以完全解决此问题:
Since Elixir 1.3 there is now Enum.group_by/3
that takes a mapper_fun
argument, which solves exactly this problem:
Enum.group_by(enumerable, &elem(&1, 0), &elem(&1, 1))
已过时的答案:
目前,标准库中没有此类功能。我最终使用了这个:
At this moment, there is no such function in the standard library. I ended up using this:
def map_group_by(enumerable, value_mapper, key_extractor) do
Enum.reduce(Enum.reverse(enumerable), %{}, fn(entry, categories) ->
value = value_mapper.(entry)
Map.update(categories, key_extractor.(entry), [value], &[value | &1])
end)
end
其中可以(以我的示例为例)这样调用:
which can (for my example) then be called like this:
map_group_by(
collection,
fn {_, second} -> second end,
fn {first, _} -> first end
)
它改编自标准库的 Enum.group_by
。
关于 [值]
:我不知道编译器可以优化或不能优化什么,但是至少这是 Enum。 group_by
也可以。
It is adapted from the standard library's Enum.group_by
.
Regarding the [value]
: I don't know what the compiler can or cannot optimize, but at least this is what Enum.group_by
does as well.
请注意 Enum.reverse
调用,但不是在我的问题的例子中。这样可以确保元素顺序保留在结果值列表中。如果您不需要保留该顺序(就像我在这种情况下所做的那样,无论如何我只想从结果中进行采样),则可以将其删除。
Note the Enum.reverse
call, which was not in the example from my question. This ensures that the element order is preserved in the resulting value lists. If you do not need that order to be preserved (like I did in my case, in which I only wanted to sample from the result anyway), it can be dropped.