[英]remove_if equivalent for std::map
我試圖根據特定條件從地圖中刪除一系列元素。 我如何使用 STL 算法做到這一點?
最初我想使用remove_if
但這是不可能的,因為 remove_if 不適用於關聯容器。
是否有任何適用於 map 的“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;
}
幾乎。
for(; iter != endIter; ) {
if (Some Condition) {
iter = aMap.erase(iter);
} else {
++iter;
}
}
如果您確實從中刪除了一個元素,那么您最初擁有的會增加迭代器兩次; 您可能會跳過需要擦除的元素。
這是我在許多地方看到使用和記錄的常用算法。
[編輯]您是正確的,迭代器在擦除后無效,但只有引用被擦除元素的迭代器,其他迭代器仍然有效。 因此在erase()
調用中使用iter++
。
我使用以下模板來做這件事。
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
});
現在, std::experimental::erase_if
在標題<experimental/map>
可用。
請參閱: http : //en.cppreference.com/w/cpp/experimental/map/erase_if
這是一些優雅的解決方案。
for (auto it = map.begin(); it != map.end();)
{
(SomeCondition) ? map.erase(it++) : (++it);
}
我從優秀的 SGI STL 參考中得到了這個文檔:
Map 有一個重要的特性,即向 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 循環中遞增一次,在擦除中又遞增一次,這可能會以某種無限循環結束。
第一的
Map 有一個重要的特性,即向 Map 中插入新元素不會使指向現有元素的迭代器失效。 從映射中擦除元素也不會使任何迭代器失效,當然,實際上指向被擦除元素的迭代器除外。
二、下面的代碼不錯
for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}
調用函數時,會在調用該函數之前評估參數。
所以當在調用erase之前對iter++求值時,迭代器的++運算符將返回當前項並指向調用后的下一項。
基於Iron Savior 的回答對於那些希望提供更多類似於 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
項並從迭代器中獲取它。
從底部注釋:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
Pair Associative Container 不能提供可變迭代器(在 Trivial Iterator 要求中定義),因為可變迭代器的值類型必須是 Assignable,而 pair 不是 Assignable。 然而,一對關聯容器可以提供不完全恆定的迭代器:這樣的迭代器 (*i).second = d 是有效的。
恕我直言,沒有remove_if()
等效項。
您無法重新排序地圖。
所以remove_if()
不能把你感興趣的對放在你可以調用erase()
的末尾。
我這樣用
std::map<int, std::string> users;
for(auto it = users.begin(); it <= users.end()) {
if(<condition>){
it = users.erase(it);
} else {
++it;
}
}
對於那些使用C++20 的人,有用於map和unordered_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;
});
如果你想擦除所有key大於2的元素,那么最好的方法是
map.erase(map.upper_bound(2), map.end());
僅適用於范圍,不適用於任何謂詞。
Steve Folly 的回答我覺得更有效率。
這是另一個簡單但效率較低的解決方案:
該解決方案使用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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.