如何解决C ++ 20系列是否支持按功能分组?
有时,根据对象的成员函数之一(getter或某种计算)的值对对象进行分组/分区非常有用。
C ++ 20范围是否启用类似的功能
std::vector<Person> {{.Age=23,.Name = "Alice"},{.Age=25,.Name = "Bob"},{.Age=23,.Name = "Chad"}};
// group by .Age and put into std::map
std::map<int/*Age is int*/,std::vector<Person>> AgeToPerson = ...;
// 23 -> Person{23,Alice},Person{23,Chad}
// 25 -> Person{25,Bob}
注1:有一个古老的question,其中可接受的答案是仅使用raw for循环
注2:range-v3具有这种令人困惑的group_by算法,这对我的任务似乎毫无用处:
给出一个源范围和一个二进制谓词,返回一个范围范围,其中每个范围都包含源中的连续元素 满足以下条件的范围:对于 与第一个元素分开的范围 传递给二进制谓词,结果为true。在本质上, views :: group_by将连续元素与二进制文件一起分组 谓词。
解决方法
使用ranges-v3
时,可以结合使用transform
和to
来实现此目的:
#include <range/v3/view/transform.hpp>
#include <range/v3/range/conversion.hpp>
#include <map>
#include <vector>
std::vector<Person> persons{
{.Age=23,.Name = "Alice"},{.Age=25,.Name = "Bob"},{.Age=23,.Name = "Chad"}};
// group by .Age and put into std::map
auto AgeToPerson = persons
| ranges::view::transform([](const auto& person)
{
return std::pair{person.Age,person};
})
| ranges::to<std::map<int,Person>>();
请记住,采用这种方法,每个年龄段只能得到一个Person
,这就是为什么您可能想使用std::multimap
而不是std::map
在以下名称组下,语言实际上提供了三种不同类型的功能:
- 采用二进制谓词(
(T,T) -> bool
)并将该谓词评估为true的连续元素分组(例如Haskell,Elixir,D,范围v3类) - 采用一元函数(
T -> U
),并使用相同的“键”对连续元素进行分组,并生成一系列U,[T]
对(例如Rust,Python,D,F#) - 采用一元函数(
T -> U
)并返回映射U: [T]
的字典(例如Clojure,Kotlin,Scala)。
前两个需要连续元素-这意味着您需要按键排序。最后一个没有,因为您仍然在生产容器。即使没有排序,您也可以从第二个开始生成第三个版本,尽管那仍然需要循环†。
但是如上所述,range-v3仅提供第1个,而C ++ 20中甚至没有。因此,您需要编写自己的东西。在这种情况下,循环可能是最好的:
template <range R,indirectly_unary_invocable<iterator_t<R>> F>
/* other requirements such that you can form a map */
auto group_by_into_map(R&& range,F&& f)
{
unordered_map<
decay_t<indirect_result_t<F&,iterator_t<R>>>,// result of unary function
vector<range_value_t<R>> // range-as-vector
> map;
for (auto&& e : range) {
map[std::invoke(f,e)].push_back(e);
}
return map;
}
某种效果。这样可以:
group_by_into_map(people,&Person::Age);
†,除非您可以使用std::unordered_multimap
。人们会使用吗?这是一个奇怪的容器。但是假设您是,那么这要容易得多。您可以编写自己的适配器:
template <typename F> // NB: must be unconstrained
auto group_by_into_map(F&& f) {
return views::transform([=](auto&& e){ return std::pair(std::invoke(f,e),e); })
| ranges::to<std::unordered_multimap>();
}
允许:
people | group_by_into_map(&Person::Age);
但这会给您unordered_multimap<int,Person>
而不是unordered_map<int,vector<Person>>
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。