W tej chwili przebijam się przez Effective STL. Punkt 5 sugeruje, że zwykle lepiej jest używać funkcji składowych zakresu niż ich odpowiedników z jednym elementem. Obecnie chcę skopiować wszystkie wartości z mapy (tj. - nie potrzebuję kluczy) do wektora.
Jaki jest najczystszy sposób na zrobienie tego?
c++
stl
containers
Gilad Naor
źródło
źródło
Odpowiedzi:
Nie możesz tutaj łatwo użyć zakresu, ponieważ iterator otrzymany z mapy odnosi się do std :: pair, gdzie iteratory, których użyjesz do wstawienia do wektora, odnoszą się do obiektu typu przechowywanego w wektorze, którym jest (jeśli odrzucasz klucz), a nie parę.
Naprawdę nie sądzę, żeby było dużo czystsze niż oczywiste:
#include <map> #include <vector> #include <string> using namespace std; int main() { typedef map <string, int> MapType; MapType m; vector <int> v; // populate map somehow for( MapType::iterator it = m.begin(); it != m.end(); ++it ) { v.push_back( it->second ); } }
który prawdopodobnie przepisałbym ponownie jako funkcję szablonu, gdybym miał go używać więcej niż raz. Coś jak:
template <typename M, typename V> void MapToVec( const M & m, V & v ) { for( typename M::const_iterator it = m.begin(); it != m.end(); ++it ) { v.push_back( it->second ); } }
źródło
Prawdopodobnie możesz użyć
std::transform
do tego celu. Wolałbym jednak wersję Neils, w zależności od tego, co jest bardziej czytelne.Przykład xtofl (patrz komentarze):
#include <map> #include <vector> #include <algorithm> #include <iostream> template< typename tPair > struct second_t { typename tPair::second_type operator()( const tPair& p ) const { return p.second; } }; template< typename tMap > second_t< typename tMap::value_type > second( const tMap& m ) { return second_t< typename tMap::value_type >(); } int main() { std::map<int,bool> m; m[0]=true; m[1]=false; //... std::vector<bool> v; std::transform( m.begin(), m.end(), std::back_inserter( v ), second(m) ); std::transform( m.begin(), m.end(), std::ostream_iterator<bool>( std::cout, ";" ), second(m) ); }
Bardzo ogólne, pamiętaj, aby dać mu kredyt, jeśli uznasz to za przydatne.
źródło
Stare pytanie, nowa odpowiedź. W C ++ 11 mamy nową pętlę for:
for (const auto &s : schemas) names.push_back(s.first);
gdzie schematy to a,
std::map
a nazwy tostd::vector
.To zapełni tablicę (nazwy) kluczami z mapy (schematy); zmień
s.first
na,s.second
aby uzyskać tablicę wartości.źródło
const auto &s
reserve()
a uzyskasz kolejny wzrost wydajności. Wraz z pojawieniem się C ++ 11 powinno to być teraz akceptowanym rozwiązaniem!Jeśli używasz bibliotek boost , możesz użyć boost :: bind, aby uzyskać dostęp do drugiej wartości pary w następujący sposób:
#include <string> #include <map> #include <vector> #include <algorithm> #include <boost/bind.hpp> int main() { typedef std::map<std::string, int> MapT; typedef std::vector<int> VecT; MapT map; VecT vec; map["one"] = 1; map["two"] = 2; map["three"] = 3; map["four"] = 4; map["five"] = 5; std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::second,_1) ); }
To rozwiązanie jest oparte na poście Michaela Goldshteyna na liście mailingowej boost .
źródło
#include <algorithm> // std::transform #include <iterator> // std::back_inserter std::transform( your_map.begin(), your_map.end(), std::back_inserter(your_values_vector), [](auto &kv){ return kv.second;} );
Przepraszam, że nie dodałem żadnego wyjaśnienia - pomyślałem, że kod jest tak prosty, że nie wymaga żadnego wyjaśnienia. Więc:
transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
ta funkcja wywołuje
unaryOperation
każdy element zinputIterator
zakresu (beginInputRange
-endInputRange
). Wartość operacji jest przechowywana woutputIterator
.Jeśli chcemy operować całą mapą - używamy map.begin () i map.end () jako zakresu wejściowego. Chcemy, aby przechowywać nasze wartości mapę do wektora - więc musimy użyć back_inserter na naszym wektorem:
back_inserter(your_values_vector)
. Back_inserter to specjalny outputIterator, który umieszcza nowe elementy na końcu podanej (jako paremeter) kolekcji. Ostatni parametr to unaryOperation - przyjmuje tylko jeden parametr - wartość inputIterator. Możemy więc użyć lambda:,[](auto &kv) { [...] }
gdzie & kv jest po prostu odniesieniem do pary elementu mapy. Więc jeśli chcemy zwrócić tylko wartości elementów mapy, możemy po prostu zwrócić kv.second:[](auto &kv) { return kv.second; }
Myślę, że to wyjaśnia wszelkie wątpliwości.
źródło
Za pomocą lambd można wykonać:
{ std::map<std::string,int> m; std::vector<int> v; v.reserve(m.size()); std::for_each(m.begin(),m.end(), [&v](const std::map<std::string,int>::value_type& p) { v.push_back(p.second); }); }
źródło
Oto co bym zrobił.
Użyłbym również funkcji szablonu, aby ułatwić konstrukcję select2nd.
#include <map> #include <vector> #include <algorithm> #include <memory> #include <string> /* * A class to extract the second part of a pair */ template<typename T> struct select2nd { typename T::second_type operator()(T const& value) const {return value.second;} }; /* * A utility template function to make the use of select2nd easy. * Pass a map and it automatically creates a select2nd that utilizes the * value type. This works nicely as the template functions can deduce the * template parameters based on the function parameters. */ template<typename T> select2nd<typename T::value_type> make_select2nd(T const& m) { return select2nd<typename T::value_type>(); } int main() { std::map<int,std::string> m; std::vector<std::string> v; /* * Please note: You must use std::back_inserter() * As transform assumes the second range is as large as the first. * Alternatively you could pre-populate the vector. * * Use make_select2nd() to make the function look nice. * Alternatively you could use: * select2nd<std::map<int,std::string>::value_type>() */ std::transform(m.begin(),m.end(), std::back_inserter(v), make_select2nd(m) ); }
źródło
Jednym ze sposobów jest użycie funktora:
template <class T1, class T2> class CopyMapToVec { public: CopyMapToVec(std::vector<T2>& aVec): mVec(aVec){} bool operator () (const std::pair<T1,T2>& mapVal) const { mVec.push_back(mapVal.second); return true; } private: std::vector<T2>& mVec; }; int main() { std::map<std::string, int> myMap; myMap["test1"] = 1; myMap["test2"] = 2; std::vector<int> myVector; //reserve the memory for vector myVector.reserve(myMap.size()); //create the functor CopyMapToVec<std::string, int> aConverter(myVector); //call the functor std::for_each(myMap.begin(), myMap.end(), aConverter); }
źródło
Dlaczego nie:
template<typename K, typename V> std::vector<V> MapValuesAsVector(const std::map<K, V>& map) { std::vector<V> vec; vec.reserve(map.size()); std::for_each(std::begin(map), std::end(map), [&vec] (const std::map<K, V>::value_type& entry) { vec.push_back(entry.second); }); return vec; }
stosowanie:
auto vec = MapValuesAsVector (anymap);
źródło
Myślałem, że tak powinno być
std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::first,_1) );
źródło
Powinniśmy użyć funkcji transform z algorytmu STL, ostatnim parametrem funkcji transformacji może być obiekt funkcji, wskaźnik funkcji lub funkcja lambda konwertująca element mapy na element wektora. Ta mapa przypadków zawiera elementy z parą typów, które muszą zostać przekonwertowane na element o typie int dla wektora. Oto moje rozwiązanie, w którym używam funkcji lambda:
#include <algorithm> // for std::transform #include <iterator> // for back_inserted // Map of pair <int, string> need to convert to vector of string std::map<int, std::string> mapExp = { {1, "first"}, {2, "second"}, {3, "third"}, {4,"fourth"} }; // vector of string to store the value type of map std::vector<std::string> vValue; // Convert function std::transform(mapExp.begin(), mapExp.end(), std::back_inserter(vValue), [](const std::pair<int, string> &mapItem) { return mapItem.second; });
źródło
Zaskoczony, nikt nie wspomniał o najbardziej oczywistym rozwiązaniu , użyj konstruktora std :: vector.
template<typename K, typename V> std::vector<std::pair<K,V>> mapToVector(const std::unordered_map<K,V> &map) { return std::vector<std::pair<K,V>>(map.begin(), map.end()); }
źródło