簡體   English   中英

remove_if 等效於 std::map

[英]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++

對於 std::map(和其他容器)的擦除_if

我使用以下模板來做這件事。

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 的人,有用於mapunordered_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM