tags:

views:

2296

answers:

6

Multimap essentially has groups of data sorted by the key. I want a method by which I could access these individual groups and get their aggregate values. For example, in a std::multimap< string, int > I store

{"Group1", 1}, {"Group1", 2}, {"Group1", 3},

{"Group2", 10}, {"Group2", 11}, {"Group2", 12}

Having stored these values, I should be able to iterate this multimap and get the aggregate values of each "group". Problem is there aren't any functions defined in STL to access MultiMaps in such a way. I could use lower_bound, upper_bound to manually iterate the multimap and total the group's contents, but I am hopng there could be better ways already defined in STL ? Can anyone propose a solution as to how I could get the aggregate values for a group in the above example.

+5  A: 

If you already know the keys, you can use multimap::equal_range to get the iterators to the beginning and end of the group; use any standard algorithm to get the desired results from the range. If you don't know the keys, you can start at begin() and iterate through them yourself, comparing keys to find the start of each new group.

Mark Ransom
+10  A: 
pair<Iter, Iter> range = my_multimap.equal_range("Group1");
int total = accumulate(range.first, range.second, 0);

Is one way.

Edit:

If you don't know the group you are looking for, and are just going through each group, getting the next group's range can be done like so:

template <typename Pair>
struct Less : public std::binary_function<Pair, Pair, bool>
{
    bool operator()(const Pair &x, const Pair &y) const
    {
        return x.first < y.first;
    }
};

Iter first = mmap.begin();
Iter last = adjacent_find(first, mmap.end(), Less<MultimapType::value_type>());
Greg Rogers
A: 

uh I see .. so what you're saying is there is no way to get the available keys or the "groups" in the multimap without manually doing it ?

the_Saint
It wouldn't be very difficult to write a wrapper to do just that.
Leon Timmermans
That is exactly what I'm saying - the only way to find the keys in a map or multimap is to iterate through the whole thing. Since you need each element to do your aggregate anyway, it doesn't seem like a problem.
Mark Ransom
But remember that multimaps are trees, so these functions aren't doing linear searches.
Max Lybbert
+1  A: 

Not a multimap answer, but you can do things like the following if you so choose.

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
using namespace std;
using namespace boost;
using namespace boost::assign;

int main() {
    typedef map<string, vector<int> > collection;
    collection m;
    m["Group 1"] = list_of(1)(2)(3);
    m["Group 2"] = list_of(10)(11)(12);
    collection::iterator g2 = m.find("Group 2");
    if (g2 != m.end()) {
        BOOST_FOREACH(int& i, g2->second) {
            cout << i << "\n";
        }
    }
}
Shadow2531
A: 

You can use an alternate container that can contain the aggregate sums of each group. To do this you might do something like:

template <class KeyType, class ValueType>
struct group_add {
  typedef map<KeyType, ValueType> map_type;
  map_type & aggregates;
  explicit group_add(map_type & aggregates_)
    : aggregates(aggregates_) { };
  void operator() (map_type::value_type const & element) {
    aggregates[element.first] += element.second;
  };
};

template <class KeyType, class ValueType>
group_add<KeyType, ValueType>
make_group_adder(map<KeyType, ValueType> & map_) {
  return group_add<KeyType, ValueType>(map_);
};

// ...
multimap<string, int> members;
// populate members
map<string, int> group_aggregates;
for_each(members.begin(), members.end(),
  make_group_adder(group_aggregates));
// group_aggregates now has the sums per group

Of course, if you have Lambda's (in C++0x) it could be simpler:

multimap<string, int> members;
map<string, int> group_aggregates;
for_each(members.begin(), members.end(),
  [&group_aggregates](multimap<string, int>::value_type const & element) {
    group_aggregates[element.first] += element.second;
  }
  );
Dean Michael
+4  A: 
// samekey.cpp -- Process groups with identical keys in a multimap

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

typedef multimap<string, int> StringToIntMap;
typedef StringToIntMap::iterator mapIter;

int main ()
{
    StringToIntMap mymap;

    mymap.insert(make_pair("Group2", 11));
    mymap.insert(make_pair("Group1",  3));
    mymap.insert(make_pair("Group2", 10));
    mymap.insert(make_pair("Group1",  1));
    mymap.insert(make_pair("Group2", 12));
    mymap.insert(make_pair("Group1",  2));

    cout << "mymap contains:" << endl;

    mapIter m_it, s_it;

    for (m_it = mymap.begin();  m_it != mymap.end();  m_it = s_it)
    {
        string theKey = (*m_it).first;

        cout << endl;
        cout << "  key = '" << theKey << "'" << endl;

        pair<mapIter, mapIter> keyRange = mymap.equal_range(theKey);

        // Iterate over all map elements with key == theKey

        for (s_it = keyRange.first;  s_it != keyRange.second;  ++s_it)
        {
           cout << "    value = " << (*s_it).second << endl;
        }
    }

    return 0;

}   //  end main

// end samekey.cpp