remove_if等效于std :: map

aJ.*_*aJ. 113 c++ stl map

我试图根据特定情况从地图中删除一系列元素.我如何使用STL算法?

最初我想使用remove_if但不可能因为remove_if不适用于关联容器.

是否有适用于地图的"remove_if"等效算法?

作为一个简单的选项,我想到循环遍历地图并擦除.但是循环遍历地图并删除安全选项?(因为迭代器在擦除后变为无效)

我使用以下示例:

bool predicate(const std::pair<int,std::string>& x)
{
    return x.first > 2;
}

int main(void) 
{

    std::map<int, std::string> aMap;

    aMap[2] = "two";
    aMap[3] = "three";
    aMap[4] = "four";
    aMap[5] = "five";
    aMap[6] = "six";

//      does not work, an error
//  std::remove_if(aMap.begin(), aMap.end(), predicate);

    std::map<int, std::string>::iterator iter = aMap.begin();
    std::map<int, std::string>::iterator endIter = aMap.end();

    for(; iter != endIter; ++iter)
    {
            if(Some Condition)
            {
                            // is it safe ?
                aMap.erase(iter++);
            }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*lly 108

几乎.

for(; iter != endIter; ) {
     if (Some Condition) {
          aMap.erase(iter++);
     } else {
          ++iter;
     }
}
Run Code Online (Sandbox Code Playgroud)

如果你从中删除了一个元素,那么你最初会将迭代器增加两次 ; 你可能会跳过需要删除的元素.

这是我见过的常用算法,在许多地方都有使用和记录.

[编辑]你是正确的,擦除后迭代器失效,但只有迭代器引用被擦除的元素,其他迭代器仍然有效.因此在erase()调用中使用iter ++.

  • 注意,在C++ 11中,所有关联容器(包括`map`)都从`erase(iter)`返回下一个迭代器.做'iter = erase(iter)`要干得多. (82认同)
  • @taxilian(年末)while()或for()可以工作,但从语义上讲,人们经常使用for()来迭代已知范围,而while()则使用未知数量的循环.由于在这种情况下已知范围(从开头到**endIter**),因为()不是一个不寻常的选择,并且可能更常见.但同样,两者都可以接受. (10认同)
  • 我糊涂了; 为什么你会用(; ...;)而不是while(...)?此外,虽然这可能有效,但是.erase不会返回下一个迭代器吗?所以似乎if(Some Condition)博客应该是iter = aMap.erase(iter)才能最兼容.也许我错过了什么?我缺乏一些经验. (4认同)
  • @taxilian更重要的是:使用'for',你可以让你的迭代器定义在循环的范围内,所以它不会弄乱你的程序的其余部分. (4认同)

Iro*_*ior 70

erase_if for std :: map(和其他容器)

我使用以下模板来做这件事.

namespace stuff {
  template< typename ContainerT, typename PredicateT >
  void erase_if( ContainerT& items, const PredicateT& predicate ) {
    for( auto it = items.begin(); it != items.end(); ) {
      if( predicate(*it) ) it = items.erase(it);
      else ++it;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这不会返回任何内容,但它将从std :: map中删除项目.

用法示例:

// 'container' could be a std::map
// 'item_type' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
  return /* insert appropriate test */;
});
Run Code Online (Sandbox Code Playgroud)

第二个例子(允许你传入一个测试值):

// 'test_value' is value that you might inject into your predicate.
// 'property' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4;  // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
  return item.property < test_value;  // or whatever appropriate test
});
Run Code Online (Sandbox Code Playgroud)

  • 将在 [C++20 for `std::map`](https://en.cppreference.com/w/cpp/container/map/erase_if) 等中添加。 (7认同)
  • +1使用适当的可重用代码正确回答:) (4认同)
  • @CodeAngry谢谢 - 这对我来说似乎很奇怪,这在`std`中并不存在.我理解为什么它不是`std :: map`的成员,但我觉得它应该在标准库中. (3认同)

Mar*_*nBG 9

对于C++20上的用户,有用于mapunordered_map的内置std::erase_if函数:

std::unordered_map<int, char> data {{1, 'a'},{2, 'b'},{3, 'c'},{4, 'd'},
                                    {5, 'e'},{4, 'f'},{5, 'g'},{5, 'g'}};
 
const auto count = std::erase_if(data, [](const auto& item) {
    auto const& [key, value] = item;
    return (key & 1) == 1;
});
Run Code Online (Sandbox Code Playgroud)


use*_*272 7

现在,std::experimental::erase_if在 header 中可用<experimental/map>

请参阅:http : //en.cppreference.com/w/cpp/experimental/map/erase_if

  • 现在是 C++20 (2认同)

小智 5

这是一些优雅的解决方案。

for (auto it = map.begin(); it != map.end();)
{   
    (SomeCondition) ? map.erase(it++) : (++it);
}
Run Code Online (Sandbox Code Playgroud)

  • 这依赖于 it++ 在调用 map.erase(...) 之前完全评估(当然,它确实如此)_并且_它依赖于 map.erase(...) 不会使除擦除之外的任何迭代器无效。这一切都是真的。我只是认为值得详细说明,以防有人在不知不觉中将此模式转移到另一个容器中。 (2认同)