I l@ve RuBoard Previous Section Next Section

Exercise 3.3

Define a map for which the index is the family surname and the key is a vector of the children's names. Populate the map with at least six entries. Test it by supporting user queries based on a surname and printing all the map entries.

The map uses a string as an index and a vector of strings for the children's names. This is declared as follows:



map< string, vector<string> > families; 

To simplify the declaration of the map, I've defined a typedef to alias vstring as an alternative name for a vector that contains string elements. (You likely wouldn't have thought of this because the typedef mechanism is not introduced until Section 4.6. The typedef mechanism allows us to provide an alternative name for a type. It is generally used to simplify the declaration of a complex type.)



#include <map> 


typedef vector<string> vstring; 


map< string, vstring > families; 

We get our family information from a file. Each line of the file stores the family name and the name of each child:



surname child1 child2 child3 ... childN 

Reading the file and populating the map are accomplished in populate_map():



void populate_map( ifstream &nameFile, map<string,vstring> &families ) 


{ 


   string textline; 


   while ( getline( nameFile, textline )) 


   { 


       string fam_name; 


       vector<string> child; 


       string::size_type 


           pos = 0, prev_pos = 0, 


           text_size = textline.size(); 





       // ok: find each word separated by a space 


       while ((  pos = textline.find_first_of( ' ', pos )) 


                    != string::npos ) 


       { 


            // figure out end points of the substring 


            string::size_type end_pos = pos - prev_pos; 





            // if prev_pos not set, this is the family name 


            // otherwise, we are reading the children ... 


            if ( ! prev_pos ) 


                 fam_name = textline.substr( prev_pos, end_pos ); 


            else child.push_back(textline.substr(prev_pos,end_pos)); 


            prev_pos = ++pos; 


       } 





       // now handle last child 


       if ( prev_pos < text_size ) 


            child.push_back(textline.substr(prev_pos,pos-prev_pos)); 





       if ( ! families.count( fam_name )) 


            families[ fam_name ] = child; 


       else cerr << "Oops! We already have a " 


                 << fam_name << " family in our map!\n"; 


    } 


} 

getline() is a standard library function. It reads a line of the file up to the delimiter specified by its third parameter. The default delimiter is the newline character, which is what we use. The line is placed in the ssecond parameter string.

The next portion of the function pulls out, in turn, the family surname and the name of each child. substr() is a string class operation that returns a new string object from the substring delimited by the two position arguments. Finally, the family is entered into the map if one is not already present.

To display the map, we define a display_map() function. It prints entries in this general format:



The lippman family has 2 children: danny anna 

Here is the display_map() implementation:



void display_map( const map<string,vstring> &families, ostream &os ) 


{ 


   map<string,vstring>::const_iterator 


                it = families.begin(), 


                end_it = families.end(); 





   while ( it != end_it ) 


   { 


         os << "The " << it->first << " family "; 


         if ( it->second.empty() ) 


              os << "has no children\n"; 


         else 


         {  // print out vector of children 


            os   << "has " << it->second.size()  << " children: "; 


            vector<string>::const_iterator 


                   iter = it->second.begin(), 


                   end_iter = it->second.end(); 


            while ( iter != end_iter ) 


                  { os << *iter << " "; ++iter; } 


            os << endl; 


         } 


         ++it; 


    } 


} 

We must also allow users to query the map as to the presence of a family. If the family is present, we display the family name and number of children in the same way that display_map() does for the entire map. (As an exercise, you might factor out the common code between the two functions.) I've named this function query_map().



void query_map( const string &family, 


                const map<string,vstring> &families ) 


{ 


    map<string,vstring>::const_iterator 


         it = families.find( family ); 





    if ( it == families.end() ){ 


         cout << "Sorry. The " << family 


              << " is not currently entered.\n"; 


         return; 


    } 





    cout << "The " << family; 


    if ( ! it->second.size() ) 


         cout << " has no children\n"; 


    else { // print out vector of children 


          cout << " has " << it->second.size() << " children: "; 


          vector<string>::const_iterator 


                  iter = it->second.begin(), 


                  end_iter = it->second.end(); 


          while ( iter != end_iter ) 


                { cout << *iter << " "; ++iter; } 


          cout << endl; 


     } 


} 

The main() program is implemented as follows:



int main() 


{ 


   map< string, vstring > families; 


   ifstream nameFile( "C:\\My Documents\\families.txt" ); 





   if ( ! nameFile ) { 


         cerr << "Unable to find families.txt file. Bailing Out!\n"; 


         return; 


   } 


   populate_map( nameFile, families ); 





   string family_name; 


   while ( 1 ){ //!! loop until user says to quit ... 


      cout << "Please enter a family name or q to quit "; 


      cin >> family_name; 


      if ( family_name == "q" ) 


           break; 


      query_map( family_name, families ); 


   } 


   display_map( families ); 


} 

The families.txt file contains the following six entries:



lippman danny anna 


smith john henry frieda 


mailer tommy june 


franz 


orlen orley 


ranier alphonse lou robert brodie 

When compiled and executed, the program generates the following results. My responses are highlighted in bold.



Please enter a family name or q to quit ranier 


The ranier family  has 4 children: alphonse lou robert brodie 


Please enter a family name or q to quit franz 


The franz family  has no children 


Please enter a family name or q to quit kafka 


Sorry. The kafka family is not currently entered. 


Please enter a family name or q to quit q 


The franz family has no children 


The lippman family has 2 children: danny anna 


The mailer family has 2 children: tommy june 


The orlen family has 1 children: orley 


The ranier family has 4 children: alphonse lou robert brodie 


The smith family has 3 children: john henry frieda 
    I l@ve RuBoard Previous Section Next Section