擦除std :: map中的特定元素

时间:2011-08-10 08:23:03

标签: c++ algorithm stl map containers

我想删除std :: map中的一些元素 我写了erase + remove_if技术,我总是使用其他序列容器 但它不是用地图编译的。为什么呢?
我怎么能做这个工作?

std::map<int, int> m;

bool foo(const std::pair<int, int>& p)
{
    return p.second > 15;
}

int _tmain(int argc, _TCHAR* argv[])
{
    m.insert(make_pair(0, 0));
    m.insert(make_pair(1, 10));
    m.insert(make_pair(2, 20));
    m.insert(make_pair(3, 30));

    m.erase(
        remove_if(m.begin(), m.end(), foo),
        m.end()); // compile error

    return 0;
}

6 个答案:

答案 0 :(得分:16)

这样写地图,因为remove_if不适用于map迭代器(它只是将违规元素放在最后,而map迭代器不允许这样做):

template <typename Map, typename F>
void map_erase_if(Map& m, F pred)
{
    typename Map::iterator i = m.begin();
    while ((i = std::find_if(i, m.end(), pred)) != m.end())
        m.erase(i++);
}

或者如果你喜欢单行:

template <typename Map, typename F>
void map_erase_if(Map& m, F pred)
{
    for (typename Map::iterator i = m.begin();
         (i = std::find_if(i, m.end(), pred)) != m.end();
         m.erase(i++));
}

答案 1 :(得分:7)

因为std::map不是“序列容器”:) remove_if将尝试将无用的元素放在地图的末尾,但这会导致违反地图的隐式数据结构(大多数情况下为红黑树)。隐式数据结构定义了地图中每个元素的位置,这就是remove_if不允许std::map的原因。

你应该循环擦除std::map中的元素(或给出一些间隔)。

有点像这样:

it = m.begin();
while ((it = std::find_if(it, m.end(), pred)) != m.end())
    m.erase(it++);

答案 2 :(得分:5)

“使用其他序列容器”是您的错误 - map关联容器!在关联容器中,元素由它们的键定义(而不是它们在序列容器中的插入顺序),并且您按键擦除元素

m.erase(12);

按键值擦除具有与查找相同的复杂度(例如,映射的O(log n),无序映射的O(1)等)。或者,您可以在恒定时间内通过迭代器擦除。擦除迭代器会使迭代器无效,但不会使其他迭代器失效(再次与序列容器不同),因此如果要迭代映射,典型的习惯用法就是:

for (auto it = m.cbegin(); it != m.cend(); ) // no "++"!
{
  if (it->second > 15)  // your own condition goes here
  {
    m.erase(it++);
  }
  else
  {
    ++it;
  }
}

答案 3 :(得分:1)

这个成语仅适用于像容器这样的序列 - 地图中的条目(关联)无法重新排序(键不会改变 - 所以你可能期望移动一个条目到某个其他位置 - 例如结束)。正确的方法是找到条目并将其删除 - 即it = map.find(); map.erase(it++)

答案 4 :(得分:0)

尝试这样的事情

#include <iostream>
#include <map>
#include <algorithm>

class foo 
{
public:
    enum CompType { GREATER=1, LESS=-1 };
    foo(int nVal=15, enum CompType ctype=GREATER)
    : m_nVal(nVal)
    , m_bGreater(ctype==GREATER)
    {
    }
    bool operator()(std::pair<int, int> p) 
    {
        if (m_bGreater)
            return p.second > m_nVal;
        else
            return p.second < m_nVal;
    }
private:
    int  m_nVal;
    bool m_bGreater;
};

void MapRemove(std::map<int, int> &m, foo &pred)
{
    auto itr = std::find_if(m.begin(), m.end(), pred);
    while (itr != m.end())
        itr = std::find_if(m.erase(itr), m.end(), pred);
}

int main(int argc, char *argv[])
{
    std::map<int, int> m;

    m.insert(std::make_pair(0, 0));
    m.insert(std::make_pair(1, 10));
    m.insert(std::make_pair(2, 20));
    m.insert(std::make_pair(3, 30));

    MapRemove(m, foo());

    for (auto itr=m.begin(); itr!=m.end(); ++itr)
        std::cout << "(" << itr->first << ", " 
                  << itr->second << ")" << '\n';

    return 0;
}

答案 5 :(得分:0)

  

但它不是用地图编译的。为什么呢?

使用remove_if时,解除引用的迭代器类型必须满足CopyAssignable的要求。也就是说,应该可以将一个值分配给另一个值。

对于std::map<int, int>,值为std::pair<const int, int>,表示map的键值对,而不是CopyAssignable。关键字const int的原因是地图如何在内部工作,就像其他人已经指出的那样。

顺便说一下,你会得到序列容器的相同编译错误,如下所示:

std::vector<std::pair<const int, int>>;