4

In C++, how would I swap two elements of a map?

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
wrongusername
  • 18,564
  • 40
  • 130
  • 214

6 Answers6

7

The provided answers are correct but are using operator[] twice for the same keys, which isn't free and could be avoided :

std::map<char, std::string> a;

Solution 1 :

std::string &item1 = a['a'];
std::string &item2 = a['b'];
std::swap(item1, item2);

Solution 2 :

const std::map<char, std::string>::iterator item1 = a.find('a');
const std::map<char, std::string>::iterator item2 = a.find('b');
if ((item1 != a.end()) && (item2 != a.end()))
    std::swap(item1->second, item2->second);

Of course, the two solutions aren't equivalent (solution 2 only swaps values which are already in the map, solution 1 inserts without questioning and might end up swapping two default constructed strings).

icecrime
  • 74,451
  • 13
  • 99
  • 111
  • Is solution 1 safe? I can't find anything in the STL docs to say that a reference to a value in a map won't get invalidated by an insertion, though it appears to be Ok in g++. – Pete Kirkham Nov 20 '10 at 10:53
  • @Pete I think it is, 23.1.2/8 "The insert members shall not affect the validity of iterators and references to the container" – icecrime Nov 20 '10 at 10:56
3

None of the answers provided so far deal with nonexistant keys and preserving the nonexistance of these keys.

template<class Key, class Value>
void swap_map_elements(std::map<Key, Value>& map, const Key& key1, const Key& key2)
{
    auto it1 = map.find(key1);
    auto it2 = map.find(key2);
    auto end = map.end();

    if(it1 != end && it2 != end) {
        std::swap(it1->second, it2->second);
    }
    else if(it1 != end) {
        map.emplace(std::make_pair(key2, std::move(it1->second)));
        map.erase(key1);
    }
    else if(it2 != end) {
        map.emplace(std::make_pair(key1, std::move(it2->second)));
        map.erase(key2);
    }
}

Example:

auto M = std::map<int, std::string>();
M.emplace(std::make_pair(1, "one"));
M.emplace(std::make_pair(2, "two"));

swap_map_elements(M, 1, 2); // 1: "two", 2: "one"
swap_map_elements(M, 1, 4); // 2: "one", 4: "two"
swap_map_elements(M, 5, 2); // 4: "two", 5: "one"
swap_map_elements(M, 8, 9); // 4: "two", 5: "one"
Animiral
  • 31
  • 2
3

I think std::swap() is a good choice.

Kylo
  • 2,300
  • 1
  • 19
  • 24
  • Unfortunately `std::map::iterator` is a const iterator, preventing the user from modifying the sorted order outside of the class. You have to cast away the `const`-ness with a `const_cast` first to perform an `std::swap`. – plasmacel Oct 31 '17 at 14:23
2
  auto mymap = std::map<int, char>{{1, 'a'}, {2, 'b'}};
  std::swap(mymap.at(1), mymap.at(2));
Eugene
  • 6,194
  • 1
  • 20
  • 31
Oleksandr Kozlov
  • 697
  • 6
  • 11
0

What do you mean by swap in a map? A normal plain vanila map doesn't have any particular order so speaking of swapping has no meaning in reference to order.

If you are looking for C++ map implementation that preserves order, in which case ordering becomes meaningful then look here

If however you are looking to swap the value associated with one key with the value associated with a second key then just do

map<char, string> myMap;

myMap['a'] = "firstValue";
myMap['b'] = "secondValue";

/* Now let's swap */
string tmpString = myMap['a'];
myMap['a'] = myMap['b'];
myMap['b'] = tmpString
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
hhafez
  • 38,949
  • 39
  • 113
  • 143
  • Ah yes, I forgot that there isn't really an order associated with maps... thanks for pointing that out! – wrongusername Nov 20 '10 at 20:01
  • `std::map` does maintain a sorted order, this is why it is implemented as a self-balancing binary search tree. You maybe confused it with `std::unordered_map`, which is basically a hash map - without any particular order. – plasmacel Oct 31 '17 at 14:02
0

Do you mean this?

const T tmp = map["a"];
map["a"] = map["b"];
map["b"] = tmp;
Draco Ater
  • 20,820
  • 8
  • 62
  • 86