开发者

Populate a vector with all multimap values with a given key

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开发者_如何学C 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?


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, ","));

}


Let's go lambda

given: multimap<A,B> M

requested: vector<B> (of all values in M with a specific key 'a'.)

method:

std::pair<M::iterator, M::iterator> aRange = M.equal_range('a')
std::vector<B> aVector;
std::transform(aRange.first, aRange.second,std::back_inserter(aVector), [](std::pair<A,B> element){return element.second;});         

System environment:

  1. compiler: gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 (with -std=c++11)
  2. os: ubuntu 16.04

Code example:

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

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

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

    std::pair<MapType::iterator,MapType::iterator> aRange = m.equal_range("123");

    std::transform(aRange.first, aRange.second, std::back_inserter(v), [](std::pair<std::string,int> element){return element.second;});

    for(auto & elem: v)
    {
        std::cout << elem << std::endl;
    }
    return 0;
}


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


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


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.


Just some addenda to the other answers here…

std::mem_fn (from #include <functional>) can be used as a shorthand for the transform operator:

// previously we might've used this longhand
[](pair<int,string> element){return element.second;}

And we can use vector::resize and std::distance to allocate space for the vector in one go, rather than repeatedly resizing it with back_inserter.

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

using namespace std;

typedef multimap<int, string> MapType;

int main()
{
    MapType multimap;
    vector<string> valuesForKey123;

    multimap.emplace(0,   "red");
    multimap.emplace(123, "hello");
    multimap.emplace(123, "world");
    multimap.emplace(0,   "herring");

    MapType::iterator lower{multimap.lower_bound(123)};
    MapType::iterator upper{multimap.upper_bound(123)};
    valuesForKey123.resize(distance(lower, upper));

    transform(
        lower,
        upper,
        valuesForKey123.begin(),
        mem_fn(&MapType::value_type::second));

    copy(
        valuesForKey123.begin(),
        valuesForKey123.end(),
        ostream_iterator<string>(cout, " "));
}
// outputs "hello world "
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜