remove_if相当于std :: map
我试图根据特定的条件从地图中删除一系列元素。 我如何使用STLalgorithm做到这一点?
最初我想过使用remove_if
但是这是不可能的,因为remove_if不适用于关联容器。
有没有任何“remove_if”等效algorithm适用于map?
作为一个简单的选项,我想通过循环地图和擦除。 但是通过地图循环并擦除一个安全的选项?(因为迭代器在擦除后变得无效)
我用下面的例子:
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; }
几乎。
for(; iter != endIter; ) { if (Some Condition) { aMap.erase(iter++); } else { ++iter; } }
如果你已经从中删除了一个元素,那么你最初会增加迭代器两次 ; 您可能会跳过需要擦除的元素。
这是我见过的很多地方常用的algorithm。
[编辑]你是正确的,迭代器是无效擦除后,但只有迭代器引用被删除的元素,其他迭代器仍然有效。 因此在erase()调用中使用iter ++。
erase_if为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 */; });
第二个例子(允许你传入一个testing值):
// '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 });
我从优秀的SGI STL参考中获得了这个文档:
Map具有将新元素插入到地图中的重要属性不会使指向现有元素的迭代器无效。 擦除地图中的元素也不会使任何迭代器失效,当然,除了实际上指向正被擦除的元素的迭代器。
所以,你指向被删除元素的迭代器当然是无效的。 做这样的事情:
if (some condition) { iterator here=iter++; aMap.erase(here) }
原来的代码只有一个问题:
for(; iter != endIter; ++iter) { if(Some Condition) { // is it safe ? aMap.erase(iter++); } }
这里iter
在for循环中增加一次,在擦除中又增加一次,这可能会在一些无限循环中结束。
基于铁救主的答案对于那些想要提供一个范围更多沿着stdfunction线迭代器。
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项目,并从迭代器中获取。
从底部的笔记:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
对联合容器不能提供可变迭代器(如Trivial Iterator需求中定义的那样),因为可变迭代器的值types必须是可赋值的,并且对不是可赋值的。 然而,一个Pair Associative Container可以提供不完全一致的迭代器:迭代器使得expression式(* i).second = d是有效的。
第一
Map具有将新元素插入到地图中的重要属性不会使指向现有元素的迭代器无效。 擦除地图中的元素也不会使任何迭代器失效,当然,除了实际上指向正被擦除的元素的迭代器。
其次,下面的代码是好的
for(; iter != endIter; ) { if(Some Condition) { aMap.erase(iter++); } else { ++iter; } }
调用函数时,在调用该函数之前评估参数。
所以当iter ++在擦除调用之前求值时,迭代器的++运算符将返回当前项目,并在调用之后指向下一个项目。
恕我直言,没有remove_if()
等效。
您无法重新排列地图。
所以remove_if()
不能把你感兴趣的对放在你可以调用erase()
那一端。
史蒂夫·福利的回答我觉得更有效率。
这是另一个简单但是效率较低的解决scheme :
该解决scheme使用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);
如果你想删除键大于2的所有元素,那么最好的办法是
map.erase(map.upper_bound(2), map.end());
仅适用于范围,但不适用于任何谓词。
现在, std::experimental::erase_if
可以在头文件<experimental/map>
。
请参阅: http : //en.cppreference.com/w/cpp/experimental/map/erase_if