Thread: using maps<>

  1. #1
    Registered User hex_dump's Avatar
    Join Date
    Dec 2012
    Posts
    88

    using maps<>

    I have the following code
    Code:
    //p.345 ex1
    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    #include <list>
    #include <map>
    
    #define RND_RANGE 10000
    #define TIMES (RND_RANGE)
    
    using namespace std;
    
    int main(void){
    
        srand(time(NULL));
    
        //generate 10,000 numbers between 0 and 9,999
        list<int> the_list;
        list<int>::iterator iter;
    
        //place in list<int> container
        for(int i = 0; i < TIMES; i++){
            int j = (rand() % RND_RANGE);
            the_list.push_back(j);
        }
    
        //iterate over values and display them
        int x = 0;
        for(iter = the_list.begin(); iter !=the_list.end(); iter++){
            cout << "item: " << x << " = " << *iter << "..." << endl;
            if(x % 10 == 0){
                cout << "Press [ENTER] for next ten...";
                cin.get();
            }
            
            x++;
        }
    
        //create a map container to hold frequency of each number
        map<int, int> frequency;
        map<int, int>::iterator iter1, iter2;
        
        //iterate over each element in the list
        int outer = 0;
        for(iter1 = the_list.begin(); iter1 !=the_list.end(); ++iter1){
            cout << "outer num: " << outer << endl;
            //for each element in the list on the first time
            //create a key/value pair where k=num/v=frequency, v=0
            frequency[*iter1] = 0; 
            outer++;
    
            //iterate the list again while at current element to see if it repeates
            int inner = 0;
            for(iter2 = the_list.begin(); iter2 !=the_list.end(); ++iter2){
                cout << "outer : " << outer << " inner : " << inner << endl;
                //if it occurs again increase the v count by 1
                if( *iter2 == *iter1) frequency[*iter1]+=1; 
            }
        }
            
        //what is the number that appears most
        //what is the number that appears least
        
        return 0;
    }
    it has a bunch of errors which I can post if you'd like but essentially seems to have to do with my code from the map<> decalaration. I'd like to just insert into the map on the elements first occurance and then increment it's value on subseuent occurance. I saw the following code in my book
    Code:
    // Associative Containers - looking up ages
    
    #include <iostream>
    #include <map>
    #include <string>
    using namespace std;
    
    int main()
    {
       map<string, int, less<string> > name_age;
    
       name_age["Pohl,Laura"] = 12;
       name_age["Dolsberry,Betty"] = 39;
       name_age["Pohl,Tanya"] = 14;
       cout << "Laura is " << name_age["Pohl,Laura"] 
            << " years old." << endl;
    }
    so assumed I could do it thus without using insert() however my compiler isn't agreeing.

  2. #2
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    please post the errors.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  3. #3
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    the problem is that you're trying to iterate over a list<int> with map<int, int> iterators. that will never work. they are not compatible types.

    your logic is also flawed in that every time you encounter a number the same number in the list, you're resetting the count in the map to zero.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  4. #4
    Registered User hex_dump's Avatar
    Join Date
    Dec 2012
    Posts
    88
    Quote Originally Posted by Elkvis View Post
    the problem is that you're trying to iterate over a list<int> with map<int, int> iterators. that will never work. they are not compatible types.
    wow, that was retarded on my part. Thank you!
    your logic is also flawed in that every time you encounter a number the same number in the list, you're resetting the count in the map to zero.
    Okay thank you, I'll check that and repost.
    Thanks again.

  5. #5
    Registered User hex_dump's Avatar
    Join Date
    Dec 2012
    Posts
    88
    here is the new code. I'm really just getting used to the containers in the STL so please bear with me. Any comments would be appreciated as a have a little bit of semantic satiation right now
    Code:
    //p.345 ex1
    //generates N numbers to a list<>
    //finds num of occurances of values
    //place in map<> where k=number/v=frequency
    
    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    #include <list>
    #include <map>
    
    #define RND_RANGE 10000
    #define TIMES 1000
    
    using namespace std;
    
    int main(void){
    
        srand(time(NULL));
    
        //generate 10,000 numbers between 0 and 9,999
        list<int> the_list;
        list<int>::iterator iter;
    
        //place in list<int> container
        for(int i = 0; i < TIMES; i++){
            int j = (rand() % RND_RANGE);
            the_list.push_back(j);
        }
    
        //iterate over values and display them
        int x = 0;
        for(iter = the_list.begin(); iter !=the_list.end(); iter++){
            cout << "item: " << x << " = " << *iter << "..." << endl;
            if(x % 10 == 0){
                cout << "Press [ENTER] for next ten...";
                cin.get();
            }
            x++;
        }
    
        //create a map container to hold freq of each number
        map<int, int> freq;
        list<int>::iterator iter1, iter2;
        
        //iterate over each element in the list
        //foreach(auto elem& : the_list)
        int outer = 0;
        for(iter1 = the_list.begin(); iter1 !=the_list.end(); ++iter1){
            //cout << "outer num: " << outer << endl;
            //for each element in the list on the first time
            //create a key/value pair where k=num/v=freq, v=0
            freq[*iter1] = 0; 
    
            //iterate the list again while at current element to see if it repeates
            int inner = 0;
            for(iter2 = the_list.begin(); iter2 !=the_list.end(); ++iter2){
                //cout << "outer : " << outer << " inner : " << inner;
                //if it occurs again increase the v count by 1
                if( *iter2 == *iter1){ 
                    freq[*iter1]+=1;
                }
            inner++;
            }
            cout << endl;
            outer++;
        }
        
        //iterate over map to see key/value pairs
        map<int,int>::iterator map_itr;
        if(freq.size() > 0){
    
            cout << "Number\tFrequency" << endl;
            for(map_itr = freq.begin(); map_itr !=freq.end(); ++map_itr){
                cout << (*map_itr).first << " appears " << map_itr->second << " times" << endl;
            }
        }
    
        else
            cout << "No duplicates found in list container..." << endl;    
    
        //what is the number that appears most
        //what is the number that appears least
        
        return 0;
    }

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    you're still resetting the count of each number to zero. you should change line 53 to something like this:
    Code:
    if (freq.find(*iter1) == freq.end()) freq[*iter1] = 0;
    this will only set it to zero if it didn't find it in the map.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  7. #7
    Registered User hex_dump's Avatar
    Join Date
    Dec 2012
    Posts
    88
    hey, sorry I've been away for a little bit. I'm not sure exactly where I'm resetting the count of each number.
    Code:
     //p.345 ex1
    //generates N numbers to a list<>
    //finds num of occurances of values
    //place in map<> where k=number/v=frequency
    
    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    #include <list>
    #include <map>
    
    #define RND_RANGE 10000
    #define TIMES 100
    
    using namespace std;
    
    int main(void){
    
        srand(time(NULL));
    
        //generate 10,000 numbers between 0 and 9,999
        list<int> the_list;
        list<int>::iterator iter;
    
        //place in list<int> container
        for(int i = 0; i < TIMES; i++){
            int j = (rand() % RND_RANGE);
            the_list.push_back(j);
        }
    
        //iterate over values and display them
        int x = 0;
        for(iter = the_list.begin(); iter !=the_list.end(); iter++){
            cout << "item: " << x << " = " << *iter << "..." << endl;
            if(x % 10 == 0){
                cout << "Press [ENTER] for next ten...";
                cin.get();
            }
            x++;
        }
    
        //create a map container to hold freq of each number
        map<int, int> freq;
        list<int>::iterator iter1, iter2;
        
        //iterate over each element in the list
        for(iter1 = the_list.begin(); iter1 !=the_list.end(); ++iter1){
            //create a key/value pair where k=num/v=freq, v=0
            freq[*iter1] = 0; 
    
            //iterate the list again while at current element to see if it repeates
            for(iter2 = the_list.begin(); iter2 !=the_list.end(); ++iter2){
                //if it occurs again increase the v count by 1
                if( *iter2 == *iter1){ 
                    freq[*iter1]+=1;
                }
            }
        }
        
        //iterate over map to see key/value pairs
        map<int,int>::iterator map_itr;
        cout << "Number\tFrequency" << endl;
           for(map_itr = freq.begin(); map_itr !=freq.end(); ++map_itr){
                cout << (*map_itr).first << " appears " << map_itr->second << " times" << endl;
            }
    
        
        return 0;
    }
    In the above code the first loop creates a key value pair for the number in the list and assigns it to be zero. ...the second loop iterates from the beginning and increments the value in the map if that number is found again. So with the way i have it each number is guaranteed to say it appears at least once. I'm not sure what I'm missing so please spell it out if need be.

    also I understand the usage of the method find() but the second loop accomplishes that.
    Last edited by hex_dump; 05-18-2013 at 04:08 PM. Reason: clarification

  8. #8
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    This nested loops

    Code:
        //iterate over each element in the list
        for(iter1 = the_list.begin(); iter1 !=the_list.end(); ++iter1){
            //create a key/value pair where k=num/v=freq, v=0
            freq[*iter1] = 0; 
    
            //iterate the list again while at current element to see if it repeates
            for(iter2 = the_list.begin(); iter2 !=the_list.end(); ++iter2){
                //if it occurs again increase the v count by 1
                if( *iter2 == *iter1){ 
                    freq[*iter1]+=1;
                }
            }
        }
    could be simplified to

    Code:
        for(iter1 = the_list.begin(); iter1 !=the_list.end(); ++iter1)
            ++freq[*iter1];
    Kurt

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I redid the code a little to bring it up to modern C++ (including C++11):
    Code:
    template<typename C, typename Elem, typename... T> void push_back(C& c, const Elem& value, const T&... values)
    {
    	c.push_back(value);
    	push_back(c, values...);
    }
    
    template<typename C> void push_back(C&) {}
    
    int main()
    {
    	const int RndRange = 10000;
    	const int Time = 10;
    
    	std::srand(std::time(nullptr));
    
    	//generate 10,000 numbers between 0 and 9,999
    	std::list<int> the_list;
    	push_back(the_list, 1, 2, 2, 3, 4, 2, 3, 3, 5, 1, 0);
    
    	//   //place in list<int> container
    	//   for (int i = 0; i < Time; i++)
    	//       the_list.push_back((rand() % RndRange));
    
    	//iterate over values and display them
    	{
    		int item_c = 0;
    		for (const auto & item : the_list)
    			std::cout << "item: " << ++item_c << " = " << item << "..." << std::endl;
    	}
    
    	//create a map container to hold freq of each number
    	std::map<int, int> freq;
    
    	//iterate over each element in the list
    	for (const auto & item : the_list)
    		freq[item]++;
    
    	//iterate over map to see key/value pairs
    	std::cout << "Number\tFrequency" << std::endl;
    	for (const auto & item : freq)
    		std::cout << item.first << " appears " << item.second << " times" << std::endl;
    
    	return 0;
    }
    Thing to take home:
    - Don't use #define. Use const T, where T is a type.
    - Don't use NULL. Use nullptr if you can (C++11).
    - No need for global variables, even if they're constants. Limit scope.
    - No need to declare iterators before the loops. Declare them inside the loops so you don't have to poison the function scope.
    - Use range based for if you can (C++11).
    - Use auto to deduce the types for iterators automatically, if possible (C++11).
    - Use the find method of map to see if an element exists, and if not, insert it (initialize it to 0). If you want to be more efficient, you can initialize it to one and skip incrementing it.
    - No need to use += for increments of 1. Use the ++ operator!
    - operator [] constructs a new element with a default-constructed value with the specified key if it doesn't exist. That means that if you try to access some key k, and it doesn't exist, it will be created with value 0. Therefore, you really don't have to initialize anything. You can just increment away directly! Efficient and easy.
    - The global push_back function isn't really necessary to understand or know. It's a helper function to help inserting values into the list (instead of randomizing it for easier testing). Visual Studio's support for initializer lists is poor, which is why I had to do it. In gcc or clang, you'd use initializer lists to initialize it (unless you do your loop to randomize the values inserted, of course). The global push_back function uses variadic templates. Variadic templates and initializer lists are both C++11 features.
    Last edited by Elysia; 05-18-2013 at 05:52 PM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User hex_dump's Avatar
    Join Date
    Dec 2012
    Posts
    88
    okay, so back to the original question then: I wasn't re-initializing the values to zero was I?

    Not bad...i like what you did on lines 35 and 36...I'm assuming this is what checks to see if a value occurs and then increments its value? I copied and pasted it but Vim made a mess of it so it didn't compile.

    couple gripes (on my n00b C++ part):

    -the push_back method only works on sequence containers...not associative ones so this is guaranteed to fail on a map or set right?
    -so considering the aforementioned, and the fact that we're still invoking the push_back method, what's the point of putting in a function?
    -again considering that we created a template function to do that work, why seed the SRNG? since we never actually used it.
    -I'm still new to C++ but any reason for the braces on lines 25 thru 29?
    -and lastly something I've been meaning to find out...what is with the convention of using the prefix notation on loops in C++?

    I think that's it, thank you for posting that some of them I had an idea of but still learning and others like the "....." ability to add variable length arguments I did not know.
    Thanks again, to both you and Zuk.


    edit: hmm...i think you answered the question as to the reason for the push_back function.

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by hex_dump View Post
    Not bad...i like what you did on lines 35 and 36...I'm assuming this is what checks to see if a value occurs and then increments its value? I copied and pasted it but Vim made a mess of it so it didn't compile.
    The [] operator inserts a default-constructed value (for integers, it's 0) if it doesn't exist. So it creates it if it doesn't exist and initialized it to 0, then returns it which is incremented. If it exists, then the existing element is returned.
    As for the compile errors, make sure your compiler supports C++11 and that your compiler is set to follow it (gcc, for example, needs the -std=c++11 switch). If it doesn't support C++11, this code won't compile.

    -the push_back method only works on sequence containers...not associative ones so this is guaranteed to fail on a map or set right?
    I don't see push_back on maps and sets

    -so considering the aforementioned, and the fact that we're still invoking the push_back method, what's the point of putting in a function?
    That was just a small hack I used for easily inserting values when testing. Msvc's standard library doesn't support initializer lists. If I didn't use it, I'd have to do

    the_list.push_back(1);
    the_list.push_back(2);
    the_list.push_back(2);
    //...

    -again considering that we created a template function to do that work, why seed the SRNG? since we never actually used it.
    Again, that was just for my testing. If you intend to randomize values for inserting, you definitely need it.

    -I'm still new to C++ but any reason for the braces on lines 25 thru 29?
    To stop the item_c variable from poisoning the function scope. I only intend to use it within the loop, but there's no way to declare variables inside a range-based for loop (it can only be done inside, at which point they'll be destroyed on every iteration, or outside). You can't declare two different variables with two different types inside normal for loops, either, hence the extra scope.

    -and lastly something I've been meaning to find out...what is with the convention of using the prefix notation on loops in C++?
    For iterators, prefix is faster because the postfix operator must construct a temporary of itself and return it (remember that the value returned by postfix is the value before it's incremented?). Prefix does not such thing, so other than that, it's a matter of style, pretty much.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Hang on:
    Code:
    template<typename C, typename Elem, typename... T> void push_back(C& c, const Elem& value, const T&... values)
    {
        c.push_back(value);
        push_back(c, values...);
    }
    
    
        std::list<int> the_list;
        push_back(the_list, 1, 2, 2, 3, 4, 2, 3, 3, 5, 1, 0);
    Does this do what I think it does? It is NOT NOT NOT a good idea to fill a list by using a function with 10000 arguments.

    Use, I don't know, generate(), or something:

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by whiteflags View Post
    Does this do what I think it does? It is NOT NOT NOT a good idea to fill a list by using a function with 10000 arguments.
    If you mean that it recursively calls the function once for each argument? Then yes.
    But then again, would you type out 10 000 arguments in the call to that function? I sure as heck wouldn't. And that's really the only use of it, anyway.

    But then again, that got me thinking. Why use variadic templates when all arguments are of the same type?
    If that's the case, then the follow would suffice:

    Code:
    template<typename C, typename Elem> void push_back(C& c, std::initializer_list<Elem> values)
    {
    	for (const auto & elem : values)
    		c.push_back(elem);
    }
    
    auto args = { 1, 2, 2, 3, 4, 2, 3, 3, 5, 1, 0 };
    push_back(the_list, args);
    (Again, C++11 features.)

    EDIT2:
    But then again, it might be a good idea to use r-values in the function since I would be passing temporaries in most of the cases (that's really what I designed it for), so a better version might be:

    Code:
    template<typename C, typename Elem, typename... T> void push_back(C& c, const Elem& value, const T&&... values)
    {
    	c.push_back(value);
    	push_back(c, std::move(values)...);
    }
    
    template<typename C> void push_back(C& c) {}
    Last edited by Elysia; 05-18-2013 at 06:19 PM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    I'm just not sure what purpose this serves other than you showing off. The data isn't even random anymore.

  15. #15
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    It was for testing purposes.
    It is easier to insert 10 specific values and check that it holds or insert 100 random values and check it holds.
    But yeah, the reason for having it remain the code I showed off is ... pretty much because I wanted to show it off. And give a little taste of the power in C++.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. map of maps ??
    By howhy in forum C++ Programming
    Replies: 1
    Last Post: 09-06-2005, 07:00 AM
  2. Maps :)
    By Hunter2 in forum Game Programming
    Replies: 4
    Last Post: 09-25-2003, 09:31 AM
  3. STL - Maps
    By jhebert in forum C++ Programming
    Replies: 1
    Last Post: 09-03-2003, 05:13 PM
  4. DLL's and Maps
    By nickname_changed in forum C++ Programming
    Replies: 7
    Last Post: 09-01-2003, 10:23 PM
  5. how do i use maps?
    By jverkoey in forum C++ Programming
    Replies: 2
    Last Post: 03-15-2003, 02:13 PM