STL में वेक्टर के लिए मानचित्र मान कॉपी करें


85

इस समय प्रभावी एसटीएल के माध्यम से अपने तरीके से काम करना। आइटम 5 से पता चलता है कि आमतौर पर अपने एकल तत्व समकक्षों के लिए सीमा सदस्य कार्यों का उपयोग करना बेहतर होता है। मैं वर्तमान में एक नक्शे में सभी मानों को कॉपी करना चाहता हूं (यानी - मुझे सदिश की आवश्यकता नहीं है)।

ऐसा करने का सबसे साफ तरीका क्या है?


यदि कुंजियों की आवश्यकता नहीं है, तो पूरे नक्शे की भी आवश्यकता नहीं हो सकती है। ऐसे मामले में नक्शे से वेक्टर में मानों को स्थानांतरित करने पर विचार करें जैसा कि इस प्रश्न में वर्णित है ।
न्यकोडियम

जवाबों:


61

आप आसानी से यहां एक सीमा का उपयोग नहीं कर सकते हैं। मैप से प्राप्त होने वाला पुनरावृत्त एक std :: जोड़ी को संदर्भित करता है, जहाँ आप पुनरावृत्त वेक्टर में सम्मिलित करने के लिए उपयोग करेंगे, जो वेक्टर में संग्रहीत प्रकार की एक वस्तु को संदर्भित करता है, जो है (यदि आप कुंजी छोड़ रहे हैं) एक जोड़ी नहीं।

मुझे नहीं लगता कि यह स्पष्ट की तुलना में बहुत साफ हो जाता है:

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

अगर मैं इसे एक से अधिक बार उपयोग करने जा रहा था, तो शायद मैं एक टेम्पलेट फ़ंक्शन के रूप में फिर से लिखूंगा। कुछ इस तरह:

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

78
पायथन ने मुझे सचमुच बिगाड़ दिया है :-(
गिलाद नोर

1
अच्छा लगा, खाका। शायद यह एक कंटेनर के बजाय एक आउटपुट पुनरावृत्ति दे!
xtofl

Skurmedel का समाधान और भी अच्छा है: ap -> p.second functor के साथ 'ट्रांसफ़ॉर्म' फ़ंक्शन का उपयोग करें।
xtofl

2
मैं ओकाम के रेजर में दृढ़ विश्वास रखता हूं - संस्थाओं को अनिवार्य रूप से पेश नहीं करता। परिवर्तन समाधान के मामले में, हमें एक सहायक फ़ंक्शन की आवश्यकता है जो स्पष्ट लूप समाधान पर आवश्यक नहीं है। इसलिए जब तक हमें नाममात्र के कार्य नहीं मिलेंगे, मैं अपने समाधान के साथ रहूँगा।

3
ओकाम की रेजर व्याख्या से सावधान रहें। एक नया गैर-कास्ट वैरिएबल "इट" पेश करना अंत में सबसे सुरक्षित उपाय नहीं हो सकता है। एसटीएल एल्गोरिदम काफी समय के लिए तेजी से और मजबूत साबित हुए हैं।
विंसेंट रॉबर्ट

62

आप शायद std::transformउस उद्देश्य के लिए उपयोग कर सकते हैं । मैं शायद नील संस्करण को पसंद करूंगा, जो कि अधिक पठनीय है, उसके आधार पर।


Xtofl द्वारा उदाहरण (टिप्पणियां देखें):

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

यदि आप इसे उपयोगी पाते हैं, तो बहुत सामान्य, उसे श्रेय देना याद रखें।


कि मुझे नील से भी अच्छा लगता है। वर्कआउट, वर्कआउट!
xtofl

मैं अंतिम पैरामीटर के लिए लैम्ब्डा का उपयोग करने का सुझाव दूंगा।
varepsilon

@varepsilon: संभवतः एक अच्छा विचार (यदि कोई आधुनिक C ++ कंपाइलर पर है), लेकिन मैं C ++ के साथ अब और आश्वस्त नहीं हूं, मैं इन दिनों एक C दोस्त हूं। अगर कोई इसे सुधारना चाहता है और सोचता है कि वे इसे कर सकते हैं, तो कृपया आगे बढ़ें :)
स्कर्मेडेल

29

पुराना सवाल, नया जवाब। C ++ 11 के साथ हमारे पास लूप के लिए फैंसी नया है:

for (const auto &s : schemas)
   names.push_back(s.first);

जहां स्कीमा एक है std::mapऔर नाम एक है std::vector

यह मानचित्र (स्कीमा) से कुंजियों के साथ सरणी (नाम) को पॉप्युलेट करता है; मूल्यों की एक सरणी प्राप्त s.firstकरने के s.secondलिए परिवर्तन ।


3
यह होना चाहिएconst auto &s
स्लाव

1
@ स्लाव किसी भी एक नई सीमा के लिए स्पष्ट करने के लिए सीमा के आधार पर: जिस तरह से मैंने इसे लिखा है वह काम करता है, हालांकि, स्लाव का सुझाव दिया गया संस्करण तेज़ है और सुरक्षित है क्योंकि एक संदर्भ का उपयोग करके पुनरावृत्ति ऑब्जेक्ट को कॉपी करने से बचा जाता है, और एक कास्ट निर्दिष्ट करता है क्योंकि यह होगा पुनरावृत्ति को संशोधित करने के लिए खतरनाक। धन्यवाद।
सेठ

4
सबसे छोटा और साफ समाधान। और शायद सबसे तेज़ (स्वीकृत समाधान की तुलना में तेज़ होने के लिए और @ अरगोर्नक्स के समाधान से भी तेज़)। जोड़ें reserve()और आपको एक और प्रदर्शन लाभ मिलेगा। C ++ 11 के आगमन के साथ जो अब स्वीकृत समाधान होना चाहिए!
एड्रियन डब्ल्यू

3
यह नाम नहीं होना चाहिए। push_back (s.second); जैसा कि सवाल मानों के लिए पूछता है, न कि एक वेक्टर में चाबियाँ?
डेविड

24

यदि आप बूस्ट लाइब्रेरी का उपयोग कर रहे हैं , तो आप बूस्ट का उपयोग कर सकते हैं :: जोड़ी के दूसरे मूल्य तक पहुँचने के लिए बाइंड इस प्रकार है:

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

यह समाधान बूस्ट मेलिंग सूची में माइकल गोल्डसेन के एक पोस्ट पर आधारित है ।


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

क्षमा करें कि मैंने कोई स्पष्टीकरण नहीं जोड़ा - मुझे लगा कि कोड इतना सरल है कि किसी भी स्पष्टीकरण की आवश्यकता नहीं है। इसलिए:

transform( beginInputRange, endInputRange, outputIterator, unaryOperation)

यह फ़ंक्शन रेंज ( - ) unaryOperationसे प्रत्येक आइटम पर कॉल करता है । ऑपरेशन के मूल्य में संग्रहीत किया जाता है ।inputIteratorbeginInputRangeendInputRangeoutputIterator

यदि हम पूरे मानचित्र के माध्यम से काम करना चाहते हैं - हम अपने इनपुट रेंज के रूप में map.begin () और map.end () का उपयोग करते हैं। हम वेक्टर में हमारे नक्शे मान संग्रहीत करना चाहते हैं - इसलिए हम अपने वेक्टर पर back_inserter उपयोग करने के लिए: back_inserter(your_values_vector)। Back_inserter विशेष आउटपुट है जो दी गई (paremeter) संग्रह के अंत में नए तत्वों को धकेलता है। अंतिम पैरामीटर अपरिपक्वता है - यह केवल एक पैरामीटर लेता है - इनपुटइंटरेटर का मान। इसलिए हम लैम्ब्डा का उपयोग कर सकते हैं: [](auto &kv) { [...] }जहां और केवी केवल आइटम के जोड़े को मैप करने के लिए एक संदर्भ है। इसलिए यदि हम मानचित्र की वस्तुओं के केवल मूल्यों को वापस करना चाहते हैं, तो हम केवल kv.second लौटा सकते हैं:

[](auto &kv) { return kv.second; }

मुझे लगता है कि यह किसी भी संदेह की व्याख्या करता है।


3
नमस्ते, कोड के साथ थोड़ा स्पष्टीकरण जोड़ दें क्योंकि यह आपके कोड को समझने में मदद करता है। कोड केवल जवाब पर frowned हैं।
भार्गव राव

1
हाँ! यह कोड स्निपेट प्रश्न को हल कर सकता है, स्पष्टीकरण सहित वास्तव में आपकी पोस्ट की गुणवत्ता में सुधार करने में मदद करता है। याद रखें कि आप भविष्य में पाठकों के लिए प्रश्न का उत्तर दे रहे हैं, और वे लोग आपके कोड सुझाव के कारणों को नहीं जान सकते हैं।
जे। चोमेल

मुझे लगता है कि यह केवल C ++ 14 से शुरू होता है, क्योंकि ऑटो लैम्ब्डा से पहले समर्थित नहीं है। स्पष्ट फ़ंक्शन हस्ताक्षर अभी भी काम करेंगे।
टर्नी

19

लंबोदा के प्रयोग से निम्न कार्य कर सकते हैं:

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

1
मुझे नहीं लगता कि आपको v.reserve (m.size ()) की आवश्यकता है क्योंकि v जैसे ही आप नए तत्वों को आगे बढ़ाएंगे।
ड्रैगान ओस्टोजिक

11
@ DraganOstojić .reserve () केवल एक ही कारण बनता है। तत्वों की संख्या के आधार पर .push_back () एक ही आकार में आने के लिए कई आवंटन कर सकता है।
mskfisher

8

यहां है जो मुझे करना होगा।
इसके अलावा मैं 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)
                  );
}

1
अच्छा था। और क्यों make_select2nd stl में नहीं हैं?
मायकोला गोलूबेव

select2nd SGI संस्करण (इसलिए अनौपचारिक) में STL का एक विस्तार है। उपयोगिताओं को फंक्शन टेम्प्लेट के रूप में जोड़ना अभी दूसरी प्रकृति है (Make_pair <> () प्रेरणा के लिए देखें)।
मार्टिन यॉर्क

2

एक तरीका है फफूंद का उपयोग करना:

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

मैं चर aConverter के साथ परेशान नहीं होता। बस for_each में एक अस्थायी बनाएं। std :: for_each (myMap.begin (), myMap.end (), CopyMapToVec <std :: string, int> (myVector));
मार्टिन यॉर्क

'ट्रांसफॉर्म' को प्राथमिकता दें, क्योंकि आप जो कर रहे हैं: एक नक्शे को एक वेक्टर में बदलकर एक बहुत ही सरल फ़नकार का उपयोग कर रहे हैं।
xtofl

2

क्यों नहीं:

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

उपयोग:

ऑटो vec = MapValuesAsVector (anymap);


मुझे लगता है कि अपने vec का दो बार आकार होगा नक्शा
dyomas

धन्यवाद डियोमास, मैंने आकार बदलने के बजाय एक रिज़र्व करने के लिए फ़ंक्शन को अपडेट किया है और अब यह सही ढंग से काम करता है
Jan Wilmans

2

मुझे लगा कि यह होना चाहिए

std::transform( map.begin(), map.end(), 
                   std::back_inserter(vec), 
                   boost::bind(&MapT::value_type::first,_1) ); 

2

हमें एसटीएल एल्गोरिदम से ट्रांसफ़ॉर्म फ़ंक्शन का उपयोग करना चाहिए, ट्रांसफ़ॉर्म फ़ंक्शन का अंतिम पैरामीटर एक फ़ंक्शन ऑब्जेक्ट, फ़ंक्शन पॉइंटर या एक लैम्ब्डा फ़ंक्शन हो सकता है जो वेक्टर के आइटम के लिए नक्शे के आइटम को परिवर्तित करता है। इस केस मैप में आइटमों के प्रकार जोड़े हैं जिन्हें वेक्टर के लिए अंतर प्रकार वाले आइटम में बदलने की आवश्यकता है। यहाँ मेरा समाधान है कि मैं लंबो फ़ंक्शन का उपयोग करता हूं:

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

-3

आश्चर्यचकित किसी ने भी सबसे स्पष्ट समाधान का उल्लेख नहीं किया है , एसटीडी :: वेक्टर निर्माणकर्ता का उपयोग करें।

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

4
ऐसा इसलिए है क्योंकि आपका समाधान प्रश्न के अनुकूल नहीं है। वेक्टर में केवल मान शामिल होने चाहिए।
ypnos
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.