2010-04-02 12 views
7

Dado un multimap<A,B> M, ¿cuál es una manera ordenada de crear un vector<B> de todos los valores en M con una clave específica.Rellenar un vector con todos los valores multimapa con una clave dada

por ejemplo, dado un multimapa ¿cómo puedo obtener un vector de todas las cadenas asignadas al valor 123?

Una respuesta de es fácil, en bucle desde el límite inferior-> superior, pero ¿hay algún método ordenado sin bucles?

+0

¿Cómo se llega a todos los elementos sin bucle? – GManNickG

+0

de ahí la pregunta, buscando trucos/magia de STL (para hacer el ciclo para nosotros) –

Respuesta

4

Aquí es la manera de hacerlo al estilo 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

Entonces, select2nd no está en VC++ 2008 entonces? –

+0

Existe en los encabezados de Dinkumware que vienen con VC++ 2008, pero eso y muchos otros están dentro de un #ifdef _HAS_TRADITIONAL_STL ... #endif –

1

Se puede inicializar el vector dándole dos iteradores, así:

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

... 

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

pero que le daría un vector de pares (es decir, con la clave y valor).

Otra opción sería usar std::copy con algo así como un back_inserter, que es otra forma de ocultar el bucle, pero con el mismo inconveniente que el anterior.

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

Esto adjuntaría los elementos (si los hubiera) al vector foo.

Para extraer solo los valores, no se me ocurre otra manera que recorrer los resultados ya que no conozco una forma estándar de obtener solo el valor de un rango.

+2

Un problema es que esto creará un 'vector >' y no un 'vector ' –

+0

Gack. Tienes razón, parece que di la respuesta correcta a la pregunta incorrecta. –

2

Necesita un bucle de todos modos. Todos los métodos "sin bucle" simplemente abstraen el bucle.

#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

Bueno, por supuesto, lo resumen, ¡ese es el punto de la pregunta! Creo que tu respuesta es el tipo de cosa que estaba buscando, pero no me había dado cuenta de que select2nd no es estándar. ¿Está en MSVC++? –

+0

@John: no se puede encontrar en MSDN. Pero es fácil escribir una plantilla 'functor' typename T, typename U> U select2nd_f (const std :: pair & 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; 
} 

Esto es un poco artificial, debido a su requerimiento 'ningún bucle'.

Prefiero:

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

¿Por qué devolver una referencia? ¿Por qué limitar al usuario a trabajar solo con 1 clave a la vez? – kennytm

+0

1. ¿Por qué no? 2. Porque es lo que pidió el OP. –

2

Vamos lambda

dado: multimap<A,B> M

solicitada: vector<B> (de todos los valores en M con una clave específica 'a'.)

método:

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

sistema:

  1. compilador: gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 (con -std = C++ 11)
  2. OS: ubuntu 16,04

Código ejemplo:

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