Accessing a map by index.

May 17, 2008 at 9:03am
I want to swap random elements of a map, but I don't seem to be able to access a map other than by key.
It doesn't matter which elements are swapped, as long as there are random elements swapped.

Here is my try:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
#include <map>
using namespace std;

int main()
{
    map <const char*, int> List;
    
    List["ene"] = 1;
    List["tweede"] = 2;
    List["derde"] = 3;
    List["vierde"] = 4;
    List["vijfde"] = 5;
    
    // I want to swap the 3th and 4th item, but I don't want to access them by
    // key, but like it was an array.
    swap(List.begin()+3, List.begin()+4);
    
    for(map<const char*, int>::iterator iter = List.begin(); iter != List.end(); iter++ ) {
        cout << (*iter).first << " is " << (*iter).second << endl;
    }
}


Compile log:
Compiler: Default compiler
Bezig met uitvoeren van g++.exe...
g++.exe "C:\Documents and Settings\Laptop\Mijn documenten\map test swapping.cpp" -o "nul" -Wall -Werror -Wformat -lalleg -lgmpxx -lgmp -I"C:\Dev-Cpp\lib\gcc\mingw32\3.4.2\include" -I"C:\Dev-Cpp\include\c++\3.4.2\backward" -I"C:\Dev-Cpp\include\c++\3.4.2\mingw32" -I"C:\Dev-Cpp\include\c++\3.4.2" -I"C:\Dev-Cpp\include" -L"C:\Dev-Cpp\lib"
C:\Documents and Settings\Laptop\Mijn documenten\map test swapping.cpp: In function `int main()':
C:\Documents and Settings\Laptop\Mijn documenten\map test swapping.cpp:15: error: no match for 'operator+' in '(&List)->std::map<_Key, _Tp, _Compare, _Alloc>::begin [with _Key = const char*, _Tp = int, _Compare = std::less<const char*>, _Alloc = std::allocator<std::pair<const char* const, int> >]() + 3'
C:\Documents and Settings\Laptop\Mijn documenten\map test swapping.cpp:15: error: no match for 'operator+' in '(&List)->std::map<_Key, _Tp, _Compare, _Alloc>::begin [with _Key = const char*, _Tp = int, _Compare = std::less<const char*>, _Alloc = std::allocator<std::pair<const char* const, int> >]() + 4'

Uitvoering voltooid

Last edited on May 17, 2008 at 9:04am
May 17, 2008 at 2:27pm
there some problem with you code I think:
First :
the map type's iterator doesn't support the " + n" operator, the only why to change it is "++";
Second:
Even you tried ++ three times, but according to the template declaration
template <class T> void swap ( T& a, T& b );
the T is a typename , so it only changed the iterator only , the function do nothing
Third :
If you tried sway(* iterator, * interator), then compiler will show you a error, that the can not use the default assignment operator...

Hope this will help
May 17, 2008 at 2:51pm
Using a for loop to increment an iterator 100 times, doesn't sound fast.
Also just changing the iterator isn't what I want, I want direct access.

So how should I swap 2 random elements of a map instead? (without knowing the key in advance)

BTW: I already presumed my code isn't the right way to do it, I was just hoping to find a correct way here.
May 17, 2008 at 3:36pm
without knowing the key, then you must write the swap() yourself, for example,like this
1
2
3
int temp = (*a).second;
    (*a).second = (*b).second;
    (*b).second = temp;


the var a and b is the iterator that point to the postion you want.....
May 17, 2008 at 7:53pm
the var a and b is the iterator that point to the postion you want.....

How to get these pointers?
Last edited on May 17, 2008 at 7:54pm
May 18, 2008 at 3:44am
the only way I know is to move the iterator from begin to the position one step by one step....
like this

1
2
3
map<const char*, int>::iterator a = list.begin();
for(int i = 0; i < pos; i++)
    a++;
May 18, 2008 at 5:09pm
Typically you will use map::find() to locate the two items you want, then use the std::swap() algorithm to exchange the two elements.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <algorithm>
#include <map>
#include <utility>

typedef int t_age;

std::map<std::string,t_age> people;

people.insert( std::make_pair( std::string("Jenny"), 12 ) );
people.insert( std::make_pair( std::string("Kevin"), 10 ) );

// oops! I meant that Jenny was 10 and Kevin was 12...
std::swap(
  people.find( std::string("Kevin") )->second,
  people.find( std::string("Jenny") )->second
  );


Hope this helps.
Last edited on May 18, 2008 at 5:11pm
May 18, 2008 at 5:22pm
But what if I don't know the names Jenny and Kevin in advance? But just know that there are 2 elements inside it and I want to swap them?
May 18, 2008 at 7:41pm
That makes no sense. The whole purpose of a map is to associate a key and value(s). Other languages call it a "dictionary" or "associative array." There is no "index" order.

Your only two options are to
1. do as adir suggests and iterate through the map, element by element, or
2. do as I suggest and know the keys for the elements you want.

If neither of these work, perhaps you are using the wrong data type. What exactly are you trying to do with this?
Last edited on May 18, 2008 at 7:42pm
May 19, 2008 at 12:54am
Hold on. The whole thing makes no sense at all.

maps guarantee O(log N) lookups by storing elements in a tree in order.
You can't just go and change elements willy-nilly in a map. If you really need to do that, then map isn't the right container.

If you want to be able to do "iterator + N" then you need a container that supports random access iterators. vector would fit the bill there.

May 19, 2008 at 2:04am
The order is only defined internally to be a linear, strict-weak, increasing ordered set. From the user's point of view, there is no order. He shouldn't care.

Further, exchanging the pairs' seconds doesn't do anything to the map's internal storage. (Copy constructors work wonders.) It is not incongruous to modify a key's associated value. That is the purpose of a map: access to storage of mutable data via some form of unique key.

Lastly, maps are unique among all STL associative containers in that they do implement the [] operator.

Hope this helps.
May 19, 2008 at 11:04am
Oops, I knew I'd get flamed for my response because I realized last night while I was caulking my bathroom sink that you were trying to swap associated values, not keys, in which case the original problem could be solved using std::advance().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
#include <map>
using namespace std;

int main()
{
    map <const char*, int> List;
    
    List["ene"] = 1;
    List["tweede"] = 2;
    List["derde"] = 3;
    List["vierde"] = 4;
    List["vijfde"] = 5;
    
    // I want to swap the 3th and 4th item, but I don't want to access them by
    // key, but like it was an array.
    assert( List.size() >= 5 ); // Assuming 0-based indexing?
    map<const char*, int>::iterator i1( List.begin() );
    map<const char*, int>::iterator i2( i1 );
    std::advance( i1, 3 );
    std::advance( i2, 3 );
    swap( i1->second, i2->second );
    
    for(map<const char*, int>::iterator iter = List.begin(); iter != List.end(); iter++ ) {
        cout << (*iter).first << " is " << (*iter).second << endl;
    }

May 19, 2008 at 1:54pm
Heh, don't feel bad. I say stupid stuff all the time...
May 19, 2008 at 2:34pm
Yeah, I should've read the original post closer.
May 19, 2008 at 6:09pm
jsmith, after I added the missing bracket and tested your code I got the following output.
ene is 1
tweede is 2
derde is 3
vierde is 4
vijfde is 5

It seems like when constructing an iterator a copy of the map is made and changes made to the copy don't affect the map itself.

One solution is keeping track of all the keys with a std::list is the only solution.
Another one is using 2 lists where one works as a key list.
Another solution is iterating through the keys a random number of times but creating a new iterator every step would be to slow.
May 20, 2008 at 11:46pm
Oops. Notice in my code above lines 20 and 21 advance the iterators the same amount... That's why things didn't appear to change. Try changing one of the 3's to 2 or 4.
May 21, 2008 at 2:14pm
Thanks:
I changed the first 3 to 2.
Output:
ene is 1
tweede is 2
derde is 4
vierde is 3
vijfde is 5


Question:
Can I append items to the map after creating the iterators?
--Testing-- Yes, you can!

Question2:
Where did you get that advance() function from? It wasn't documented on this site.
Last edited on May 21, 2008 at 7:04pm
May 23, 2008 at 12:47pm
Question 1:
You can insert items into a map and remove them without invalidating iterators. This is not a true statement for all containers, however.

Question 2:
Check out http://www.sgi.com/tech/stl/advance.html

Topic archived. No new replies allowed.