looping through hashmap

  • Thread starter Christian Gollwitzer
  • Start date
C

Christian Gollwitzer

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;
}
 
D

David Rubin

Christian said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,775
Messages
2,569,601
Members
45,182
Latest member
BettinaPol

Latest Threads

Top