我试图根据特定条件从地图中删除一系列元素。我如何使用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;
}
答案 0 :(得分:107)
几乎。
for(; iter != endIter; ) {
if (Some Condition) {
aMap.erase(iter++);
} else {
++iter;
}
}
如果你从中删除了一个元素,那么你最初会增加迭代器两次;你可能会跳过需要删除的元素。
这是我见过的常用算法,并在很多地方都有记录。
[编辑]你是正确的,擦除后迭代器失效,但只有迭代器引用被擦除的元素,其他迭代器仍然有效。因此在erase()调用中使用iter ++。
答案 1 :(得分:65)
我使用以下模板。
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;
}
}