std :: transform with lambda:跳过一些项目

时间:2016-08-17 23:35:21

标签: c++ c++11 boost lambda

我有一些像

这样的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));
});

我正在使用带有lambda函数的std :: transform将矢量转换为地图。我的问题是,有时,如图所示,我不想从我的lambda函数返回任何内容,即我基本上想要跳过i并转到下一个(不向地图添加任何内容)。< / p>

有没有办法实现我的想法?如果它有帮助我可以使用提升。我想避免一个解决方案,我必须对我的向量进行预处理或后处理以过滤掉“坏”项;我只需要看一次每个项目。另外,我的实际逻辑比写入的if / else要复杂一些,所以我认为如果可能的话,将这些内容封装在这个std :: transform / lambda模型中会很好(尽管我可能正在努力实现这种模式是不可能的。)

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

3 个答案:

答案 0 :(得分:3)

您可以先使用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)); });

Live demo

答案 1 :(得分:3)

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);
}

现在只需获得一个提升或标准或标准的经验可选。让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));
  }
);

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

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返回一个范围,其中optional是一个零或一个元素范围的模型。

答案 2 :(得分:1)

您可以使用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只是将删除的项移过它返回的迭代器。