Thread: STL:Cycling through elements in a Map

  1. #1
    Registered User
    Join Date
    Oct 2005
    Posts
    13

    STL:Cycling through elements in a Map

    I have a map of my own class, accessed by a string. I need to cycle through this map and access each element in turn.

    I've read a lot about 'iterators' but been able to comprehend none of it, and have not succeeded in getting any code to compile when attempting to use them.

    Is there anything I should know to make the class work, like syntax for iterators, constructors my class should have, etc.?

    Help would be greatly appreciated.

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    What code did you try? Maybe the first and second things are confusing?
    Code:
    std::map<std::string, int> the_map;
    the_map["one"] = 1;
    the_map["two"] = 2;
    the_map["three"] = 3;
    
    for (std::map<std::string, int>::const_iterator iter = the_map.begin(), end = the_map.end();
      iter != end; ++iter)
    {
      std::cout << "Key: " << iter->first;
      std::cout << "\nValue: " << iter->second << std:endl;
    }

  3. #3
    Registered User
    Join Date
    Oct 2005
    Posts
    13
    Well, I'm confused by the whole concept of iterators, really. I've never heard of the first/second thing- but from your code I'd guess it links the interpreter to either the access variable or the contained variable.

    That may have been the problem, thanks. However, if you know of any sites that explain iterators in detail in a way that's understandable to beginners, that could avoid future problems. Also, are there any special constructors classes need to be used by iterators? So far, I've overloaded operator= for references and pointers, in order to get maps working in the first place.

    Cheers.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    There should be no need to overload operator= to get the map to work. If you did, then you are probably doing something else wrong. Perhaps you can post a small but complete and compilable example of your code so we can see why you did that.

    As far as sites that explain it, I don't know of any in particular. SGI, Dinkumware, MSDN, and RogueWave all have references for their implementations, some might have better examples and explanations than others. Most people learn by looking at examples or reading books like The C++ Standard Library by Josuttis.

    The syntax is a bit confusing, but once you "get it" it is not so bad. Basically, think of an iterator as a class that allows you to go through the objects in a container. It has built-in capability to allow you to access the data at each location in the container, and it has methods to allow you to move forward to the next item.

    In the case of a map, the items stored are key-value pairs. An std::pair is just a struct that has two members, a first and a second. In the map, the first item is the key and the second is the value. In my example, the first item is a string and the second is an int. So the iterator lets you acccess each item which in this case is a pair<std::string, int>. Then, to access the string part of the pair you use first, and to access the int part you use second.

  5. #5
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    Well, I'm confused by the whole concept of iterators, really.
    Then you should begin by learning iterators in the context of the simplest STL container: a vector.

    An "iterator" is something that represents a position in a container. Although it's not exactly correct, you can think of an iterator as a pointer to an element in a container. Hopefully, you know what a pointer is--otherwise all is lost. Then it's just a matter of learning the syntax, which can look a bit frightening, but which is actually fairly easy.

    So, let's say you declare a vector and add some elements to the vector, like so:
    Code:
    vector<int> myVec;
    
    for(int i = 0; i<10; ++i)
    {
    	myVec.push_back(i);
    }
    Now, you want to output the elements of the vector using an iterator/pointer. First, you have to declare what type of container the iterator/pointer is going to be pointing to. In this case, we want to use an iterator/pointer that points to a vector of int's:

    vector<int>

    Then you add the member access operator '::',

    vector<int>::

    Next, you specify the type of iterator you want to use. The simplest iterator is one that allows you to both read and write the vector(just like a normal pointer allows you to both access the value and change the value it points to). The name of such an iterator is simply "iterator" (vs. "const_iterator" which only allows you to read the value it points to, not change it):

    vector<int>::iterator

    That piece of code specifies the type of the variable you are going to declare--the variable will be a read/write iterator that points to a vector of ints. Now, you need a variable name. How about 'pos' for "position":

    vector<int>::iterator pos;

    That declares a variable called 'pos' of the specified type. Now you can store an iterator that points to a vector of ints in the variable pos.

    Finally, you need to create one of those strange, alien iterators to store in your variable 'pos'. Luckily, we don't have to worry about creating an iterator because all the STL containers have a begin() and end() member function which will create and return an iterator that points to the first or last element respectively. So, you can do this:

    vector<int>::iterator pos = myVec.begin();

    The result of that statement is that 'pos' will point to the first element in the vector. If you want to "iterate" through all the elements of a vector element by element, you just use '++' on pos to move it from element to element. So, that seems like a good match for a for-loop. To end the for-loop, you can use myVec.end() as the ending condition:
    Code:
    vector<int>::iterator pos = myVec.begin(); 
    
    for(; pos != myVec.end(); ++pos)
    {
    	cout<<*pos<<" ";
    }
    cout<<endl;
    A couple of points about that loop:

    1) end() returns a pointer to one past the last element
    2) Since the loop variable can't be declared succinctly like: int i = 0
    you can move the declaration out of the for-loop header and to the line before the for loop(as shown).
    3) Use ++pos rather than pos++ because it's more efficient.
    4) Since pos is like a pointer to the element, you need to dereference it to get the actual value.
    5) When you output a vector's contents, you don't need to change the vector, so it would be better to use
    a const_iterator:

    Code:
    vector<int>::const_iterator pos = myVec.begin();
    for(; pos != myVec.end(); ++pos)
    {
    	cout<<*pos<<" ";
    	/*pos = 10;  error*/
    }
    cout<<endl;
    As far as maps are concerned, you need to know what a pair<> is to use them effectively. In a map, all the elements of the container are single entities, just like with a vector. However, each entity in a map is what's called a pair<>, and each pair<> contains two elements: the key and the value. A pair<> contains the key in a member variable called 'first', and a pair<> contains the value in a member variable called 'second'. Here is an example:
    Code:
    map<string, int> stringIntMap;
    
    stringIntMap.insert( make_pair( string("john"), 31) );
    stringIntMap.insert( make_pair( string("jane"), 18) );
    stringIntMap.insert( make_pair( string("betty"), 35) );
    
    map<string, int>::iterator pos = stringIntMap.begin();
    for(; pos != stringIntMap.end(); ++pos)
    {
    	cout<<pos->first<<" "<<pos->second<<endl;
    }
    Note: a string literal(i.e. something between double quotes) is a char array and not a string type, so on my compiler I have to use the string constructor to convert a string literal to a string type.
    Last edited by 7stud; 02-22-2006 at 01:47 AM.

  6. #6
    Registered User
    Join Date
    Oct 2005
    Posts
    13
    I played around and everything seems to have sorted itself out... The operator= seems to have been unnecessary after all, and now that I know to point to iter->second->string rather than iter->string everything works.

    Thanks very much!

  7. #7
    Tropical Coder Darryl's Avatar
    Join Date
    Mar 2005
    Location
    Cayman Islands
    Posts
    503
    a note about operator overloads and maps:

    If you use a class/struct you created as the key for the map, then:

    1. You will have to define at least an operator< as the map automatically sorts the keys and therefore needs that overload.

    2. If you plan on using the find() method, then you will have to have operator== overloaded.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. deleting specific elements from map
    By l2u in forum C++ Programming
    Replies: 8
    Last Post: 10-07-2008, 02:00 PM
  2. Polynomials and ADT's
    By Emeighty in forum C++ Programming
    Replies: 20
    Last Post: 08-19-2008, 08:32 AM
  3. using realloc for a dynamically growing array
    By broli86 in forum C Programming
    Replies: 10
    Last Post: 06-27-2008, 05:37 AM
  4. Replies: 5
    Last Post: 03-06-2006, 04:02 PM
  5. Creating a map engine.
    By suzakugaiden in forum Game Programming
    Replies: 11
    Last Post: 06-21-2005, 05:06 AM