2010-04-02 5 views
7

Étant donné un multimap<A,B> M est une manière intéressante de créer un vector<B> de toutes les valeurs de M avec une clé spécifique.Remplir un vecteur avec toutes les valeurs multimap avec une clé donnée

e exemple donné un multimap comment puis-je obtenir un vecteur de toutes les chaînes mappées à la valeur 123?

Une réponse est facile, en boucle depuis la limite inférieure> supérieure, mais existe-t-il une méthode sans boucle?

+0

Comment voulez-vous arriver à tous les éléments sans boucle? – GManNickG

+0

d'où la question, à la recherche de STL trickery/magic (pour faire la boucle pour nous) –

Répondre

4

est ici la façon de le faire style 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, ",")); 

} 
+0

Donc, select2nd n'est pas dans VC++ 2008 alors? –

+0

Il existe dans les en-têtes Dinkumware fournis avec VC++ 2008, mais cela et plusieurs autres sont dans un #ifdef _HAS_TRADITIONAL_STL ... #endif –

1

Vous pouvez initialiser le vecteur en lui donnant deux itérateurs, comme ceci:

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

... 

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

mais qui vous donnerait un vecteur de paires (c.-à-avec la clé et la valeur).

Une autre option serait d'utiliser std::copy avec quelque chose comme un back_inserter, qui est une autre façon de cacher la boucle, mais avec le même inconvénient que ci-dessus.

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

Cela ajouterait les éléments (le cas échéant) au vecteur foo.

Pour extraire les valeurs uniquement, je ne peux pas penser à autre chose que de boucler sur les résultats car je ne suis pas au courant d'un moyen standard pour obtenir seulement la valeur sur une plage.

+2

Un problème est que cela va créer un «vecteur >' et non un vecteur ' –

+0

Gack. Vous avez raison, on dirait que j'ai donné la bonne réponse à la mauvaise question. –

2

Vous avez besoin d'une boucle quand même. Toutes les méthodes "sans boucle" ne font qu'abstraire la boucle.

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

Eh bien, bien sûr, ils l'abstraite, c'est le point de la question! Je pense que votre réponse est le genre de chose que je cherchais, mais je n'avais pas réalisé que select2nd n'était pas standard. Est-ce en MSVC++? –

+0

@John: Impossible de le trouver dans MSDN. Mais il est facile d'écrire un foncteur 'template U select2nd_f (const std :: paire & p) {return p.second; } ' – kennytm

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

C'est un peu artificiel en raison de votre disposition contre la boucle ».

Je préfère:

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

Pourquoi retourner une référence? Pourquoi limiter l'utilisateur à travailler sur une seule clé à la fois? – kennytm

+0

1. Pourquoi pas? 2. Parce que c'est ce que le PO a demandé. –

2

Partons lambda

donné: multimap<A,B> M

demandé: vector<B> (de toutes les valeurs de M avec une clé spécifique 'a'.)

méthode:

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

environnement du système:

  1. compilateur: gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 (avec std = C++ 11)
  2. os: ubuntu 16,04

exemple de code:

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