looping through hashmap

Discussion in 'C++' started by Christian Gollwitzer, Nov 2, 2003.

  1. Hi,

    I'm trying to loop over the elements in a hashmap of the
    STL-implementation by SGI. The point is, that because the key/value pair
    is stored as std::pair in the COntainer, the code becomes very ugly
    and unreadable soon. I'm aware that there exists the for_each-template,
    but this doesn't make the code any better because the body of the
    for-loop must then live in an extra function (consider two nested
    loops). Now I tried to mimic the "for (key in array)"-construct of AWK
    using the C++-preprocessor. I've come up with two different versions:

    This one requires GNU-extensions (it's used to declare the Iterator in
    the initializer-statement of the for loop, and simultaneously setting up
    the loop-variables), but has the advantage that in use it looks like an
    ordinary for-loop (refer to the sample program below)

    #define FOREACH(key, value, hashmap) \
    if (!hashmap.empty()) \
    for ( { typedef typeof(hashmap) __type##hashmap; \
    __type##hashmap::iterator __it##key=hashmap.begin(); \
    value=__it##key->second; key=__it##key->first; } \
    __it##key!=hashmap.end(); \
    (++__it##key!=hashmap.end()) && \
    (value=__it##key->second, &(key=__it##key->first)!=NULL) )


    This one is (should be) ANSI-C++ compliant, but requires that you
    structure the program using FOR..ENDFOR instead of curly braces (since I
    need to set the variables inside the body of the for loop). It uses
    references for the loop-variables that are automagically declared.

    #define FORREF(key, value, hashmap) \
    { typedef typeof(hashmap) __type##hashmap; \
    for (__type##hashmap::iterator __it##key=hashmap.begin(); \
    __it##key!=hashmap.end(); \
    ++__it##key!=hashmap.end() ) { \
    const __type##hashmap::key_type& key=__it##key->first; \
    __type##hashmap::data_type& value=__it##key->second;

    #define ENDFOR } }



    Are there any pitfalls? I've used the ## tokenpaste operator to create
    new variable names in order to avoid that there are identical ones for
    nested loops.

    Here is the sample application (count identical lines until eof is read)

    #include <iostream>
    #include <string>
    #include <hash_map>
    using namespace std;

    class zeroint {
    // int that initializes as 0
    //
    int value;
    public:
    zeroint(int v=0) : value(v) {}
    zeroint(const zeroint& z) { value=z.value; }
    zeroint& operator = (const zeroint& z) { value=z.value; return
    *this; }
    zeroint& operator ++ () { ++value; return *this; }
    operator const int() const { return value; }
    };


    struct hash<string>:hash<const char*> {
    size_t operator () (const string& x) const {
    return this->hash<const char*>::eek:perator () (x.c_str());
    }
    };

    typedef hash_map<string, zeroint, hash<string> > stringmap;


    int main() {
    string mist;
    stringmap wordmap;

    //read in the lines
    while (mist!="eof") {
    cin>>mist;
    ++wordmap[mist];
    }

    //output the count using the first version
    string word;
    zeroint anzahl;
    FOREACH(word, anzahl, wordmap) {
    cout<<word<<"\t"<<anzahl<<endl;
    }

    cout<<endl;

    // Application of the second version
    // increase every mapped number by 10
    FORREF(myword, numr, wordmap)
    numr=numr+10;
    ENDFOR

    // output the count using the second version
    // static_cast is necessary, because
    // there is no operator<< for zeroints
    FORREF(wortr, numr, wordmap)
    cout<<wortr<<"\t"<<static_cast<const int&>(numr)<<endl;
    ENDFOR

    return 0;
    }


    --
    Vale !
    Christianus Auriocus
     
    Christian Gollwitzer, Nov 2, 2003
    #1
    1. Advertising

  2. Christian Gollwitzer

    David Rubin Guest

    Christian Gollwitzer wrote:
    > Hi,
    >
    > I'm trying to loop over the elements in a hashmap of the
    > STL-implementation by SGI. The point is, that because the key/value pair
    > is stored as std::pair in the COntainer, the code becomes very ugly
    > and unreadable soon. I'm aware that there exists the for_each-template,
    > but this doesn't make the code any better because the body of the
    > for-loop must then live in an extra function (consider two nested
    > loops). Now I tried to mimic the "for (key in array)"-construct of AWK
    > using the C++-preprocessor. I've come up with two different versions:


    This is not such a big deal. For example, here is how I recently did it:

    (Pardon the formatting. Map is basically a hash_map of words to list of
    occurances by line number.)

    typedef hash_map<string,
    deque<int>,
    hash<string>,
    equal_to<string> > Map;

    Map wmap;
    /*...*/

    /* display word and associated line numbers */
    for(Map::iterator i=wmap.begin(); i != wmap.end(); ++i){
    cout << (*i).first << " ";
    copy((*i).second.begin(),
    (*i).second.end(),
    ostream_iterator<int>(cout, " "));
    cout << endl;
    }

    The tricky part (and perhaps the ugly part) is getting the iterators to
    the mapped value (as opposed to the key). You can wrap this in a macro,
    function, or whatever you like.

    /david

    --
    "As a scientist, Throckmorton knew that if he were ever to break wind in
    the echo chamber, he would never hear the end of it."
     
    David Rubin, Nov 3, 2003
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Aemca
    Replies:
    1
    Views:
    575
    Aemca
    Jul 22, 2003
  2. Vince Darley
    Replies:
    4
    Views:
    4,469
    emilchacko
    Mar 2, 2010
  3. Rakesh
    Replies:
    10
    Views:
    12,213
    Mike Schilling
    Apr 8, 2008
  4. Aaron
    Replies:
    2
    Views:
    542
    dhtml
    Apr 10, 2011
  5. Replies:
    5
    Views:
    288
Loading...

Share This Page