tags:

views:

240

answers:

4

Given a multimap<A,B> M what's a neat way to create a vector<B> of all values in M with a specific key.

e.g given a multimap how can I get a vector of all strings mapped to the value 123?

An answer is easy, looping from lower->upper bound, but is there a neat loop-free method?

+1  A: 

You could initialise the vector by giving it two iterators, like this:

std::multimap<std::string, std::string> bar;

...

std::vector<pair<string,string> > foo(bar.lower_bound("123"), bar.upper_bound("123"));

but that would give you a vector of pairs (ie, with both the key and value).

Another option would be to use std::copy with something like a back_inserter, which is another way to hide the loop, but with the same downside as above.

std::copy(bar.lower_bound("123"), bar.upper_bound("123"), std::back_inserter(foo));

This would append the elements (if any) to the vector foo.

For extracting the values only, I can't think of any way but to loop over the results as I'm not aware of a standard way to get only the value out of a range.

Timo Geusch
One issue is that this will create a `vector<pair<string, string> >` and not a `vector<string>`
R Samuel Klatchko
Gack. You're right, looks like I gave the correct answer to the wrong question.
Timo Geusch
+2  A: 

You need a loop anyway. All "loop-free" methods just abstract the loop away.

#include <map>
#include <vector>
#include <algorithm>
#include <ext/functional>
using namespace std;

int main () {
    multimap<int, double> mm;
    mm.insert(make_pair(1, 2.2));
    mm.insert(make_pair(4, 2.6));
    mm.insert(make_pair(1, 9.1));
    mm.insert(make_pair(1, 3.1));

    vector<double> v;
    transform(mm.lower_bound(1), mm.upper_bound(1),
              back_inserter(v), __gnu_cxx::select2nd<pair<int, double> >());
    // note: select2nd is an SGI extension.

    for (vector<double>::const_iterator cit = v.begin(); cit != v.end(); ++ cit)
        printf("%g, ", *cit);   // verify that you've got 2.2, 9.1, 3.1
    return 0;
}
KennyTM
Well of course they abstract it away, that's the point of the question! I think your asnwer is the kind of thing I was looking for, but I hadn't realised select2nd is non-standard. Is it in MSVC++?
John
@John: Can't find it in MSDN. But it's easy to write a functor `template<typename T, typename U> U select2nd_f(const std::pair<T, U> }`
KennyTM
+1  A: 

Here's the way to do it STL style :

// The following define is needed for select2nd with DinkumWare STL under VC++
#define _HAS_TRADITIONAL_STL 1

#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <map>
#include <iterator>
#include <iostream>

using namespace std;

void main()
{
    typedef multimap<string, int> MapType;
    MapType m;
    vector<int> v;

    // Test data
    for(int i = 0; i < 10; ++i)
    {
        m.insert(make_pair("123", i * 2));
        m.insert(make_pair("12", i));
    }

    MapType::iterator i = m.lower_bound("123");
    MapType::iterator j = m.upper_bound("123");

    transform(i, j, back_inserter(v), select2nd<MapType::value_type>());

    copy(v.begin(), v.end(),  ostream_iterator<int>(cout, ","));

}
rep_movsd
So, select2nd's not in VC++ 2008 then?
John
It exists in the Dinkumware headers that come with VC++ 2008, but that and several others are inside an #ifdef _HAS_TRADITIONAL_STL ... #endif
rep_movsd
A: 
template <class Key, class Val>
vector<Val>& getValues(multimap<Key, Val>& multi, Key& key)
{
    typedef multimap<Key, Val>::iterator imm;
    static vector<Val> vect;
    static struct 
    {
        void operator()(const pair<Key, Val>& p) const
        {
            vect.push_back(p.second);
        }
    } Push;

    vect.clear();
    pair<imm, imm> range = multi.equal_range(key);
    for_each(range.first, range.second, Push);
    return vect;
}

This is a bit contrived because of your 'no loop' requirement.

I prefer:

template <class Key, class Val>
vector<Val> getValues(multimap<Key, Val>& map, Key& key)
{
    vector<Val> result;
    typedef multimap<Key, Val>::iterator imm;
    pair<imm, imm> range = map.equal_range(key);
    for (imm i = range.first; i != range.second; ++i)
        result.push_back(i->second);
    return result;
}

cheers, AR

Alain Rist
Why return a reference? Why limit the user to work on only 1 key at a time?
KennyTM
1. Why not? 2. Because it's what the OP asked.
Alain Rist