2

I have code that looks essentially like this:

std::map<int, int> map1, map2;
BOOST_FOREACH(int i, map1)
{
    // do steps 1-5 here...
}
BOOST_FOREACH(int i, map2)
{
    // do steps 1-5 (identical to above) here...
}

Is there any way to concatenate the maps to eliminate the duplicate code in the second loop? Or a way to extend BOOST_FOREACH to iterate over two different maps in one go? Obviously I don't want to increase the time complexity of the program (otherwise I could just create a new map and insert into it map1 and map2). I have a feeling I am missing something rudimentary here.

ephemient
  • 198,619
  • 38
  • 280
  • 391
kmote
  • 16,095
  • 11
  • 68
  • 91
  • was it on purpose that you iterate over int? you should use pair not only int. or has boost recently gained the possibility to iterate only over the value? – Johannes Schaub - litb Apr 20 '09 at 20:48
  • If anything, I'd expect such a feature to iterate over only the key... but it doesn't work on my version of Boost either. – ephemient Apr 20 '09 at 21:00

6 Answers6

9

You could define a function:

typedef std::map<int, int> IntMap;

void doStuffWithInt(IntMap::value_type &i)
{
  // steps 1 to 5
}

BOOST_FOREACH(IntMap::value_type &i, map1)
  doStuffWithInt(i);
BOOST_FOREACH(IntMap::value_type &i, map2)
  doStuffWithInt(i);

Although in that case it might be even simpler to use std::for_each:

for_each(map1.begin(), map1.end(), doStuffWithInt);
for_each(map2.begin(), map2.end(), doStuffWithInt);
1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
3

The idea here is to write a special type of iterators to virtually merge two containers, as far as BOOST_FOREACH is concerned. Note that i am not creating a new container out of the two existing ones. I am simply jumping from the first container's end() to the second container's begin() iterator. I did not try and write the actual merged_iterator class, but although it might a bit long to write, it's not technically difficult. I am actually surprised not to have found something like that using google. I did not look for long, though !

template<typename Container>
boost::iterator_range<
  merged_iterator<Container::iterator>
  >
concat_containers( Container& c1, Container& c2 )
{
  typedef merged_iterator<typename Container::iterator> MergedIterator;
  typedef boost::iterator_range<MergedIterator> IteratorRange;
  return IteratorRange(
    MergeIterator( c1.begin(), c1.end(), c2.begin(), c2.end() ),
    MergeIterator( c2.end(), c1.end(), c2.begin(), c2.end() ) );
}

// Now use a bit of magic to define merged_iterator<...>
// And you'll be able to write

BOOST_FOREACH( std::pair<int, int> i, concat_containers( map1, map2 ) )
{
// Do whatever you want here
}
Benoît
  • 16,798
  • 8
  • 46
  • 66
  • I implemented it a couple of days ago for another question. Note that it is not proven and might require some tweaking: http://stackoverflow.com/questions/757153/concatenating-c-iterator-ranges-into-a-const-vector-member-variable-at-construc/757328#757328 – David Rodríguez - dribeas Apr 20 '09 at 22:51
  • +1 for providing a very general approach that e.g. could be made to work even across different container types. But I feel this may be overengineering for the problem at hand -- just make a function like 0800 INFORMATION suggested! :) – j_random_hacker Apr 20 '09 at 23:37
  • Just it is overkill for this particular question. I just thought i could try and have fun :) – Benoît Apr 21 '09 at 04:54
2

In addition to 1800's solution, which I would recommend, there's also various hacky solutions:

for (int stage = 0; stage < 2; stage++) {
    BOOST_FOREACH(int i, stage == 0 ? map1 : map2) {
        ...
    }
}

typedef std::map<int, int> intmap;
std::vector<intmap *> v;
v.push_back(&map1);
v.push_back(&map2);
BOOST_FOREACH(intmap *m, v) {
    BOOST_FOREACH(int i, *m) {
        ...
    }
}

Note: when I see colleagues write code like this, sometimes I am overcome by an irresistible urge to go strangle them. Use at your own risk.

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • +1 on the note: You must always consider what you get and what you loose. If you only gain compaction of the code and you loose readability, then it does not compensate. – David Rodríguez - dribeas Apr 21 '09 at 05:45
  • i also voted up, because i like both your ways of doing it. the first is quite compact and readable. the second one too. although i would change it to read intmap* v[] = { &map1, &map2 }; BOOST_FOREACH(intmap *m, v) { ... } . i think "raw" arrays are fine in this case. – Johannes Schaub - litb Apr 21 '09 at 14:34
1

The easiest way is like this:

std::map<int, int> map1, map2;
int key, value;
BOOST_FOREACH(boost::tie(key, value), boost::join(map1, map2))
{
    // do steps 1-5 here...
}

And don't worry those commas won't confuse the preprocessor because of the parenthesis.

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
0

Of the top of my head, I'd try

std::map<int, int> map1, map2;
std::map<int, int>& maps = { map1, map2 }
BOOST_FOREACH(std::map<int, int> map, maps)
  BOOST_FOREACH(int i, map)
  {
      // do steps 1-5 here...
  }
Kees-Jan
  • 518
  • 4
  • 16
0

It's explained here.

You can do this:

std::map<int,int> m;
typedef std::pair<int,int> pair_t;
BOOST_FOREACH(pair_t p, m)
Szabolcs
  • 24,728
  • 9
  • 85
  • 174
shashank.M
  • 11
  • 1