代码之家  ›  专栏  ›  技术社区  ›  aJ.

如果与std::map等效,则删除_

  •  102
  • aJ.  · 技术社区  · 15 年前

    我试图根据特定的条件从地图中删除一系列元素。如何使用STL算法?

    最初我想用 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;
    }
    
    12 回复  |  直到 15 年前
        1
  •  105
  •   Amnon    14 年前

    几乎。

    for(; iter != endIter; ) {
                if (Some Condition) {
                        aMap.erase(iter++);
                } else {
                        ++iter;
                }
    }
    

    您最初拥有的将增加迭代器 两次 如果您确实从中删除了一个元素,那么您可能会跳过需要删除的元素。

    这是一种常见的算法,我在很多地方都看到过它的使用和记录。

    [编辑]正确的做法是,在擦除后迭代器无效,但只有引用被擦除元素的迭代器,其他迭代器仍然有效。因此,在erase()调用中使用iter+。

        2
  •  64
  •   Danvil    6 年前

    删除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
    });
    
        3
  •  3
  •   1800 INFORMATION    15 年前

    我从 excellent SGI STL reference :

    地图具有重要的特性 在地图中插入新元素 不会使迭代器失效 指向现有元素。擦除 来自映射的元素也不 使任何迭代器失效,但 当然,对于迭代器 指向正在存在的元素 擦除。

    所以,指向要删除的元素的迭代器当然会失效。这样做:

    if (some condition)
    {
      iterator here=iter++;
      aMap.erase(here)
    }
    
        4
  •  2
  •   Kate Gregory    12 年前

    原始代码只有一个版本:

    for(; iter != endIter; ++iter)
    {
        if(Some Condition)
        {
            // is it safe ?
            aMap.erase(iter++);
        }
    }
    

    这里 iter 在for循环中增加一次,在erase中增加另一次,这可能会以无限循环结束。

        5
  •  1
  •   piotr    15 年前

    从下面的注释:

    http://www.sgi.com/tech/stl/PairAssociativeContainer.html

    成对关联容器不能提供可变迭代器(如琐碎迭代器要求中定义的那样),因为可变迭代器的值类型必须是可赋值的,而成对不可赋值。但是,成对的关联容器可以提供不完全恒定的迭代器:迭代器使得表达式(*i).second=d有效。

        6
  •  1
  •   Vincent    15 年前

    弗斯特

    map具有重要的属性,即向map中插入新元素不会使指向现有元素的迭代器失效。从映射中删除元素也不会使任何迭代器失效,当然,对于实际指向被删除元素的迭代器除外。

    其次,下面的代码是好的

    for(; iter != endIter; )
    {
        if(Some Condition)
        {
            aMap.erase(iter++);
        }
        else
        {
            ++iter;
        }
    }
    

    调用函数时,参数在调用该函数之前进行计算。

    因此,在调用erase之前对iter++进行计算时,迭代器的++运算符将返回当前项,并在调用之后指向下一个项。

        7
  •  1
  •   Alexis Wilke    8 年前

    我不知道有没有 remove_if() 当量。
    不能对地图重新排序。
    所以 ReaveIf() 不能把你的兴趣放在你可以打电话的那一端 erase() .

        8
  •  1
  •   Floern    7 年前

    现在, std::experimental::erase_if 在标题中可用 <experimental/map> .

    见: http://en.cppreference.com/w/cpp/experimental/map/erase_if

        9
  •  1
  •   Danvil    6 年前

    基于 Iron Savior's answer 对于那些希望沿着std函数taking迭代器提供更多范围的函数。

    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;
        }
    }
    

    好奇是否有某种方法可以丢失containerive项并从迭代器中得到它。

        10
  •  0
  •   Community Mr_and_Mrs_D    7 年前

    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
  •   Tadeusz Kopec for Ukraine yespbs    13 年前

    如果要删除键大于2的所有元素,最好的方法是

    map.erase(map.upper_bound(2), map.end());
    

    但只适用于范围,不适用于任何谓词。

        12
  •  0
  •   voltento    6 年前

    我喜欢这样

     std::map<int, std::string> users;    
     for(auto it = users.begin(); it <= users.end()) {
        if(<condition>){
          it = users.erase(it);
        } else {
        ++it;
        }
     }