【问题标题】:Populate a vector with all multimap values with a given key使用给定键填充具有所有多图值的向量
【发布时间】:2019-07-21 22:19:01
【问题描述】:

给定一个multimap<A,B> M,用特定键创建一个包含 M 中所有值的 vector<B> 的好方法是什么。

例如,给定一个多重映射,我如何获得映射到值 123 的所有字符串的向量?

一个答案很简单,从下限->上限循环,但是有没有一种简洁的无循环方法?

【问题讨论】:

  • 如何在不循环的情况下获取每个元素?
  • 因此问题,寻找 STL 诡计/魔术(为我们做循环)

标签: c++ stl


【解决方案1】:

这是 STL 风格的方法:

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

}

【讨论】:

  • 那么,select2nd 不在 VC++ 2008 中?
  • 它存在于 VC++ 2008 附带的 Dinkumware 头文件中,但它和其他几个头文件位于 #ifdef _HAS_TRADITIONAL_STL ... #endif
【解决方案2】:

我们去 lambda

给定:multimap&lt;A,B&gt; M

请求:vector&lt;B&gt;(在 M 中具有特定键“a”的所有值中。)

方法:

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

系统环境:

  1. 编译器:gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413(带有-std=c++11)
  2. 操作系统:ubuntu 16.04

代码示例:

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

【讨论】:

    【解决方案3】:

    无论如何你都需要一个循环。所有“无循环”方法都只是将循环抽象出来。

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

    【讨论】:

    • 当然他们把它抽象掉了,这就是问题的重点!我认为您的回答是我一直在寻找的那种东西,但我没有意识到 select2nd 是非标准的。是在 MSVC++ 中吗?
    • @John:在 MSDN 中找不到。但是写一个函子很容易template&lt;typename T, typename U&gt; U select2nd_f(const std::pair&lt;T, U&gt;&amp; p) { return p.second; }
    【解决方案4】:
    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;
    }
    

    由于您的“无循环”要求,这有点做作。

    我更喜欢:

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

    【讨论】:

    • 为什么要返回引用?为什么限制用户一次只能使用 1 个键?
    • 1.为什么不? 2. 因为这是 OP 要求的。
    【解决方案5】:

    您可以通过给它两个迭代器来初始化向量,如下所示:

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

    但这会给你一个成对的向量(即,键和值)。

    另一种选择是将std::copy 与类似 back_inserter 的东西一起使用,这是隐藏循环的另一种方法,但缺点与上述相同。

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

    这会将元素(如果有)附加到向量 foo。

    对于仅提取值,我想不出任何方法,只能循环遍历结果,因为我不知道仅将值超出范围的标准方法。

    【讨论】:

    • 一个问题是这将创建一个vector&lt;pair&lt;string, string&gt; &gt; 而不是vector&lt;string&gt;
    • 盖克。你是对的,看来我对错误的问题给出了正确的答案。
    【解决方案6】:

    这里只是对其他答案的一些补充......

    std::mem_fn(来自#include &lt;functional&gt;)可以用作转换运算符的简写:

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

    而且我们可以使用vector::resizestd::distance 一次性为向量分配空间,而不是用 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 "
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多