Std::transform with lambda:跳过一些项

std::transform with lambda: skip some items

本文关键字:lambda transform with Std      更新时间:2023-10-16

我有一些c++ 11代码,如

std::vector<std::string> names;
std::map<std::string, std::string> first_to_last_name_map;
std::transform(names.begin(), names.end(), std::inserter(first_to_last_name_map, first_to_last_name_map.begin()), [](const std::string& i){
    if (i == "bad")
        return std::pair<std::string, std::string>("bad", "bad"); // Don't Want This
    else
        return std::pair<std::string, std::string>(i.substr(0,5), i.substr(5,5));
});

,其中我使用std::transform与lambda函数转换向量到映射。我的问题是,有时,如图所示,我不想从我的lambda函数返回任何东西,即我基本上想跳过那个i,去下一个(不添加任何到映射)。

有没有办法达到我想要的?如果有用的话,我可以用boost。我希望避免必须对向量进行预处理或后处理以过滤掉"坏"项的解决方案;每个项目我只需要看一次。此外,我的实际逻辑比所写的if/else要复杂一些,所以我认为如果可能的话,最好把东西封装在这个std::transform/lambda模型中(尽管我想要实现的可能是这个模型不可能实现的)。

编辑:只是为了强调,我希望以最有效的方式执行此操作(有选择性地处理向量元素并将它们插入到映射中),即使这意味着不那么优雅的解决方案或大量重写。我甚至可以根据最有效的方式使用不同的地图数据类型。

template<class Src, class Sink, class F>
void transform_if(Src&& src, Sink&& sink, F&& f){
  for(auto&& x:std::forward<Src>(src))
    if(auto&& e=f(decltype(x)(x)))
      *sink++ = *decltype(e)(e);
}

现在简单地得到一个boost或std或std实验可选。让你的f返回一个optional<blah>

auto sink = std::inserter(first_to_last_name_map, first_to_last_name_map.begin());
using pair_type = decltype(first_to_last_name_map)::value_type;
transform_if(names, sink,
  [](const std::string& i)->std::optional<pair_type>{
    if (i == "bad")
      return {}; // Don't Want This
    else
      return std::make_pair(i.substr(0,5), i.substr(5,5));
  }
);

我个人的首选选项实际上已经定义了begin end。我们得到这个算法:

template<class Src, class Sink, class F>
void polymap(Src&& src, Sink&& sink, F&& f){
  for(auto&& x:std::forward<Src>(src))
    for(auto&& e:f(decltype(x)(x)))
      *sink++ = decltype(e)(e);
}

现在允许f返回一个范围,其中可选的是零或一个元素范围的模型。

您可以简单地使用std::remove_if进行第一次/最后一次传递。例如

std::vector<std::string> names;
std::map<std::string, std::string> first_to_last_name_map;
std::transform(names.begin(), 
               std::remove_if(names.begin(),
                              names.end(),
                              [](const std::string &str){
                                    return str=="bad";
                              }),
               std::inserter(first_to_last_name_map, 
                             first_to_last_name_map.begin()),
               [](const std::string& i){
                     return std::pair<std::string, std::string>(i.substr(0,5), i.substr(5,5));
               });

注意,remove_if只是将移除的项移到它返回的迭代器后面。

您可以使用boost::adaptors::filtered先过滤不需要的vector元素,然后再将其传递给transform

using boost::adaptors::filtered;
boost::transform(names | filtered([](std::string const& s) { return s != "bad"; }),
                 std::inserter(first_to_last_name_map, first_to_last_name_map.begin()),
                 [](std::string const& i) { return std::make_pair(i.substr(0,5), i.substr(5,5)); });

现场演示