remove_if等效于std :: map

时间:2009-04-29 05:05:31

标签: 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;
}

13 个答案:

答案 0 :(得分:107)

几乎。

for(; iter != endIter; ) {
            if (Some Condition) {
                    aMap.erase(iter++);
            } else {
                    ++iter;
            }
}

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

这是我见过的常用算法,并在很多地方都有记录。

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

答案 1 :(得分:65)

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

这不会返回任何内容,但会删除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 */;
});

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

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

答案 2 :(得分:3)

我从excellent SGI STL reference获得了此文档:

  

地图具有重要的属性   将新元素插入地图   不会使迭代器失效   指向现有元素。擦除   地图中的元素也没有   使任何迭代器无效,除了   当然,对于实际的迭代器   指向正在存在的元素   擦除。

因此,指向要删除的元素的迭代器当然会失效。做这样的事情:

if (some condition)
{
  iterator here=iter++;
  aMap.erase(here)
}

答案 3 :(得分:2)

原始代码只有一个问题:

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

这里iter在for循环中增加一次,在erase中再增加一次,这可能会在某个无限循环中结束。

答案 4 :(得分:2)

现在,标头std::experimental::erase_if中提供了<experimental/map>

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

答案 5 :(得分:1)

从底部注释:

http://www.sgi.com/tech/stl/PairAssociativeContainer.html

Pair关联容器不能提供可变迭代器(如Trivial Iterator要求中所定义),因为可变迭代器的值类型必须是Assignable,而pair不是Assignable。但是,Pair关联容器可以提供不完全恒定的迭代器:迭代器使得表达式(* i).second = d有效。

答案 6 :(得分:1)

恕我直言,没有remove_if()等价物 您无法重新排序地图 因此,remove_if()无法将您感兴趣的兴趣放在最后,您可以调用erase()

答案 7 :(得分:1)

第一

  

Map具有以下重要特性:将新元素插入到映射中不会使指向现有元素的迭代器无效。从地图中删除元素也不会使任何迭代器无效,当然,除了实际指向正在被删除的元素的迭代器之外。

其次,以下代码是好的

for(; iter != endIter; )
{
    if(Some Condition)
    {
        aMap.erase(iter++);
    }
    else
    {
        ++iter;
    }
}

调用函数时,将在调用该函数之前评估参数。

因此,在调用erase之前评估iter ++时,迭代器的++运算符将返回当前项,并指向调用后的下一项。

答案 8 :(得分:1)

基于Iron Savior's answer对于那些希望提供更多范围的std函数获取迭代器的范围。

template< typename ContainerT, class _FwdIt, class _Pr >
void erase_if(ContainerT& items, _FwdIt it, _FwdIt _Last, _Pr _Pred) {
    for (; it != _Last; ) {
        if (_Pred(*it)) it = items.erase(it);
        else ++it;
    }
}

好奇是否有某种方法可以丢失ContainerT项并从迭代器中获取它。

答案 9 :(得分:1)

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

for (auto it = map.begin(); it != map.end();)
{   
    (SomeCondition) ? map.erase(it++) : (++it);
}

答案 10 :(得分:0)

Steve Folly's answer我觉得效率更高。

这是另一个简单但效率低下的解决方案

解决方案使用remove_copy_if将我们想要的值复制到新容器中,然后将原始容器的内容与新容器的内容交换:

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

...
//Temporary map to hold the unremoved elements
std::map<int, std::string> aTempMap;

//copy unremoved values from aMap to aTempMap
std::remove_copy_if(aMap.begin(), aMap.end(), 
                    inserter(aTempMap, aTempMap.end()),
                    predicate);

//Swap the contents of aMap and aTempMap
aMap.swap(aTempMap);

答案 11 :(得分:0)

如果要删除键大于2的所有元素,那么最好的方法是

map.erase(map.upper_bound(2), map.end());

仅适用于范围,不适用于任何谓词。

答案 12 :(得分:0)

我这样使用

 std::map<int, std::string> users;    
 for(auto it = users.begin(); it <= users.end()) {
    if(<condition>){
      it = users.erase(it);
    } else {
    ++it;
    }
 }