is there an iterator across unique keys in a std::multimap?

前端 未结 6 1936
无人及你
无人及你 2020-11-29 08:18

Is there a simple or standard way to have a multimap iterator which iterate across unique keys in a multimap?

i.e. for a set that looks like: {1, \"a\"}, {1, \

相关标签:
6条回答
  • 2020-11-29 08:26

    Using upper_bound would result in an easy-to-read loop but each call will perform a binary tree search, resulting in an O(n log n) instead of O(n) traversal. If the difference in efficiency matters, you can structure your traversal like this:

    typedef std::multimap<std::string, int> MapType;
    MapType container;
    for (MapType::iterator it = container.begin(); it != container.end(); ) {
      std::string key = it->first;
    
      doSomething(key);
    
      // Advance to next non-duplicate entry.
      do {
        ++it;
      } while (it != container.end() && key == it->first);
    }
    
    0 讨论(0)
  • 2020-11-29 08:28

    Runnable example

    This is a slight improvement over https://stackoverflow.com/a/24212648/895245 with a runnable unit test:

    #include <cassert>
    #include <map>
    #include <vector>
    
    int main() {
    
        // For testing.
        auto m = std::multimap<int, int>{
            {1, 2},
            {1, 3},
            {2, 4}
        };
        std::vector<int> out;
    
        // The algorithm.
        auto it = m.begin();
        auto end = m.end();
        while (it != end) {
            auto key = it->first;
    
            // Do what you want to do with the keys.
            out.push_back(key);
    
            do {
                if (++it == end)
                    break;
            } while (it->first == key);
        }
    
        // Assert it worked.
        assert(out == std::vector<int>({1, 2}));
    }
    
    0 讨论(0)
  • 2020-11-29 08:30

    You can use upper_bound to increment the iterator position instead of ++:

    #include <map>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      multimap<int,string> mm;
      mm.insert(make_pair(1, "a"));
      mm.insert(make_pair(1, "lemon"));
      mm.insert(make_pair(2, "peacock"));
      mm.insert(make_pair(3, "angel"));
    
      for( auto it = mm.begin(), end = mm.end();
           it != end;
           it = mm.upper_bound(it->first)
      )
        cout << it->first << ' ' << it->second << endl;
      return 0;
    }
    

    This results in:

    1 a
    2 peacock
    3 angel
    
    0 讨论(0)
  • 2020-11-29 08:32

    As noted in the selected answer, repeated use of multimap::upper_bound leads to an O(n log n) traversal of the map. Using the external upper_bound function gives you O(n). However, you need to ensure you only compare the key of the map:

    std::multimap<int, std::string> myMap = ... ;
    const auto compareFirst = [](const std::pair<const int, std::string>& lhs, const std::pair<const int, std::string>& rhs) {
        return lhs.first < rhs.first;
    };
    
    for(auto it = myMap.begin(); it != myMap.end(); it = std::upper_bound(it, myMap.end(), *it, compareFirst)) {
        // Do stuff...
    
    }
    

    The underlying approach is essentially the same as user3701170's solution - i.e linear search - but we put the increment step in the for statement proper, not the loop's body. Aside from putting the increment where it "usually" lives, this also means any continue statements in the loop will behave as expected.

    0 讨论(0)
  • 2020-11-29 08:37

    Try equal_range:

    http://en.cppreference.com/w/cpp/container/multimap/equal_range

    That must be an exact match.

    0 讨论(0)
  • 2020-11-29 08:44

    if you have to pass over all unique keys quickly then you can use std::map instead;

    typedef std::map< KeyType, std::list< ValueType > > MapKeyToMultiValue;
    

    Insertion would be more difficult, However you can iterate over all keys without having to bother with duplicate entries. Insertion would look as follows:

    void insert_m(MapKeyToMultiValue &map, const KeyType key, const ValueType value )
    {
      auto it = map.find( key );
      if (it == map.end())
      {
         std::list<ValueType> empty;
         std::pair< MapKeyToMultiValue::iterator, bool > ret =
            map.insert( MapKeyToMultiValue::value_type( key, empty ) );
         it = ret.first;
      }
    
      it->second.push_back( value );
    }
    

    or you can make that very templated:

    template<typename KeyType, typename ValueType, 
         typename MapType = std::map< KeyType, std::list< ValueType > > >
    void insert_multi( MapType &map, const KeyType key, const ValueType value )
    {
    
      auto it = map.find( key );
      if (it == map.end())
      {
         std::list<ValueType> empty;
         std::pair< typename MapType::iterator, bool > ret =
            map.insert( typename MapType::value_type( key, empty ) );
         it = ret.first;
      }
    
      it->second.push_back( value );
    }
    

    The full test program looks as follows:

    #include <map>
    #include <list>
    #include <string>
    #include <stdio.h>
    
    typedef std::string KeyType;  
    typedef int ValueType;
    
    typedef std::map< KeyType, std::list< ValueType > >  MapKeyToMultiValue;
    
    void insert_m(MapKeyToMultiValue &map, const KeyType key, const ValueType value )
    {
      auto it = map.find( key );
      if (it == map.end())
      {
         std::list<ValueType> empty;
         std::pair< MapKeyToMultiValue::iterator, bool > ret =
            map.insert( MapKeyToMultiValue::value_type( key, empty ) );
         it = ret.first;
      }
    
      it->second.push_back( value );
    }
    
    
    template<typename KeyType, typename ValueType, 
       typename MapType = std::map< KeyType, std::list< ValueType > > >
    void insert_multi( MapType &map, const KeyType key, const ValueType value )
    {
    
      auto it = map.find( key );
      if (it == map.end())
      {
         std::list<ValueType> empty;
         std::pair< typename MapType::iterator, bool > ret =
            map.insert( typename MapType::value_type( key, empty ) );
         it = ret.first;
      }
    
      it->second.push_back( value );
    }
    
    
    int main()
    {
        MapKeyToMultiValue map;
    
    
        insert_m(map, std::string("aaa"), 1 );
        insert_m(map, std::string("aaa"), 2 );
        insert_m(map, std::string("bb"), 3 );
        insert_m(map, std::string("cc"), 4 );
    
    
        insert_multi(map, std::string("ffffd"), 1 );
        insert_multi(map, std::string("ffffd"), 2 );
        insert_multi(map, std::string("ee"), 3 );
        insert_multi(map, std::string("ff"), 4 );
    
    
        for(auto i = map.begin(); i != map.end(); ++i)
        {
          printf("%s\n", i->first.c_str() );
        }
    
    
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题