วิธีการดึงกุญแจทั้งหมด (หรือค่า) จาก std :: map และวางลงในเวคเตอร์?


246

นี่เป็นวิธีหนึ่งที่ฉันสามารถทำได้:

struct RetrieveKey
{
    template <typename T>
    typename T::first_type operator()(T keyValuePair) const
    {
        return keyValuePair.first;
    }
};

map<int, int> m;
vector<int> keys;

// Retrieve all keys
transform(m.begin(), m.end(), back_inserter(keys), RetrieveKey());

// Dump all keys
copy(keys.begin(), keys.end(), ostream_iterator<int>(cout, "\n"));

แน่นอนว่าเราสามารถดึงค่าทั้งหมดออกจากแผนที่ได้ด้วยการกำหนด functor RetrieveValuesอีกค่า

มีวิธีอื่นในการบรรลุเป้าหมายนี้อย่างง่ายดายหรือไม่? (ฉันสงสัยอยู่เสมอว่าทำไม std :: map ไม่รวมฟังก์ชั่นสมาชิกเพื่อให้เราทำเช่นนั้น)


10
วิธีการแก้ปัญหาของคุณคือสิ่งที่ดีที่สุด ...
linello

4
keys.reserve(m.size());เพียงคิดว่าฉันจะเพิ่มเสื้อนี้อยู่
Galik

คำตอบ:


176

ในขณะที่วิธีการแก้ปัญหาของคุณควรใช้งานได้มันอาจอ่านยากขึ้นอยู่กับระดับความสามารถของโปรแกรมเมอร์ของคุณ นอกจากนี้มันจะย้ายฟังก์ชันการทำงานออกไปจากไซต์การโทร ซึ่งทำให้การบำรุงรักษาทำได้ยากขึ้นเล็กน้อย

ฉันไม่แน่ใจว่าเป้าหมายของคุณคือการเอากุญแจไปเป็นเวกเตอร์หรือพิมพ์มันออกมาเพื่อให้ฉันทำทั้งสองอย่าง คุณอาจลองทำสิ่งนี้:

map<int, int> m;
vector<int> v;
for(map<int,int>::iterator it = m.begin(); it != m.end(); ++it) {
  v.push_back(it->first);
  cout << it->first << "\n";
}

หรือง่ายยิ่งขึ้นถ้าคุณใช้ Boost:

map<int,int> m;
pair<int,int> me; // what a map<int, int> is made of
vector<int> v;
BOOST_FOREACH(me, m) {
  v.push_back(me.first);
  cout << me.first << "\n";
}

โดยส่วนตัวแล้วฉันชอบรุ่น BOOST_FOREACH เพราะพิมพ์น้อยกว่าและชัดเจนมากเกี่ยวกับสิ่งที่ทำ


1
ไปหาตัวเลขฉันจะกลับมาที่นี่หลังจากการค้นหาโดย Google ขอคำตอบคือผมต้องการ :)
mpen

4
@Jere - คุณเคยทำงานด้วยจริงBOOST_FOREACHหรือ รหัสที่คุณเสนอที่นี่ผิดทั้งหมด
Manuel

2
@Jamie - เป็นอีกวิธีหนึ่ง แต่บูสต์ doc แสดงการระบุตัวแปรและประเภทของมันก่อน BOOST_FOREACH ถ้าประเภทนั้นมีเครื่องหมายจุลภาค พวกเขายังแสดงให้เห็นถึงการพิมพ์มัน ดังนั้นฉันสับสนเกิดอะไรขึ้นกับรหัสของฉัน
Jere.Jones

17
อยากรู้อยากเห็นมันจะไม่ทำให้รู้สึกถึงการตั้งค่าเวกเตอร์เพื่อป้องกันการจัดสรรขนาด?
อลัน

2
อย่าลืมที่จะทำv.reserve(m.size())เพื่อหลีกเลี่ยงการมีการปรับขนาดเวกเตอร์ในระหว่างการถ่ายโอน
Brian White

157
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
vints.reserve(mapints.size());
for(auto const& imap: mapints)
    vints.push_back(imap.first);

4
ดี ลืมit = ...begin(); it != ...endไปเลย แน่นอนที่สุดจะเป็น std :: map โดยมีเมธอดคีย์ () ส่งคืนเวกเตอร์นั้น ...
masterxilo

2
@BenHymers: ดูเหมือนว่าฉันจะได้รับคำตอบนี้เมื่อanswered Mar 13 '12 at 22:33หลายเดือนหลังจาก C ++ 11 กลายเป็น C ++
เซบาสเตียนมัค

37
@BenHymers แต่เป็นประโยชน์กับทุกคนที่อ่านคำถามตอนนี้ซึ่งเป็นสิ่งที่เกี่ยวกับ SO - ไม่เพียง แต่ช่วยเหลือผู้ถาม แต่ทุกคนอื่น
Luchian Grigore

9
สำหรับ (อัตโนมัติ & imap) มีความแม่นยำมากขึ้นเพราะไม่มีการคัดลอก
HelloWorld

2
@StudentT for(auto const & imap : mapints)ดีกว่ายัง
cp.engr

61

มีอะแดปเตอร์ช่วงเพิ่มสำหรับวัตถุประสงค์นี้:

vector<int> keys;
// Retrieve all keys
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));

มีอะแดปเตอร์ช่วง map_values ​​ที่คล้ายกันสำหรับการแยกค่า


1
น่าเสียดายดูเหมือนว่าboost::adaptorsจะไม่สามารถใช้ได้จนกว่า Boost 1.43 การปล่อยเสถียร Debian (Squeeze) ปัจจุบันมีเฉพาะ Boost 1.42
Mickaël Le Baillif

2
ที่น่าเสียดาย. Boost 1.42 เปิดตัวในเดือนกุมภาพันธ์ 2010 นานกว่า 2.5 ปีก่อนที่จะบีบ
Alastair

ณ จุดนี้ไม่ควรบีบการอัปเดตและหรือ Backport repo เสนอ Boost 1.44?
Luis Machuca

ส่วนหัวบูสเตอร์ใดที่กำหนดไว้ใน
James Wierzba

1
ดู doco ที่เชื่อมโยงซึ่งกำหนดไว้ในboost/range/adaptor/map.hpp
Alastair

46

C ++ 0x ทำให้เรามีทางออกที่ยอดเยี่ยมและยอดเยี่ยม:

std::vector<int> keys;

std::transform(
    m_Inputs.begin(),
    m_Inputs.end(),
    std::back_inserter(keys),
    [](const std::map<int,int>::value_type &pair){return pair.first;});

22
ในมุมมองของฉันไม่มีอะไรที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้ std :: vector <int> คีย์; keys.reserve (m_Inputs.size ()); สำหรับ (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } ดีกว่าการแปลงลับ แม้ในเรื่องของประสิทธิภาพ อันนี้ดีกว่า
Jagannath

5
คุณสามารถจองขนาดของปุ่มได้ที่นี่เช่นกันหากคุณต้องการประสิทธิภาพที่เทียบเท่า ใช้การแปลงถ้าคุณต้องการหลีกเลี่ยงการวนรอบ
DanDan

4
เพียงแค่ต้องการเพิ่ม - สามารถใช้ [] (const auto & pair)
ivan.ukr

@ ivan.ukr คุณใช้คอมไพเลอร์อะไร? ไม่อนุญาตให้ใช้ไวยากรณ์นี้ที่นี่: 'const auto &': พารามิเตอร์ไม่สามารถมีประเภทที่มี 'auto'
Gobe

4
@ ivan.ukr พารามิเตอร์อัตโนมัติในแลมบ์ดาคือ c ++ 14
roalz

16

@ DanDan คำตอบโดยใช้ C ++ 11 คือ:

using namespace std;
vector<int> keys;

transform(begin(map_in), end(map_in), back_inserter(keys), 
            [](decltype(map_in)::value_type const& pair) {
    return pair.first;
}); 

และการใช้ภาษา C ++ 14 (ตามที่ระบุไว้โดย @ ivan.ukr) เราสามารถแทนที่ด้วยdecltype(map_in)::value_typeauto


5
คุณสามารถเพิ่มkeys.reserve(map_in.size());ประสิทธิภาพได้
Galik

ฉันพบว่าวิธีการแปลงใช้รหัสมากกว่า for-loop
user1633272

const สามารถอยู่ข้างหลังประเภท! ฉันเกือบลืมไปแล้ว
จาง


10

โซลูชันของคุณใช้ได้ดี แต่คุณสามารถใช้ตัววนซ้ำเพื่อทำสิ่งต่อไปนี้:

std::map<int, int> m;
m.insert(std::pair<int, int>(3, 4));
m.insert(std::pair<int, int>(5, 6));
for(std::map<int, int>::const_iterator it = m.begin(); it != m.end(); it++)
{
    int key = it->first;
    int value = it->second;
    //Do something
}

10

อิงตามโซลูชัน @ rusty-Parks แต่ใน c ++ 17:

std :: map <int, int> items;
std :: vector <int> itemKeys;

สำหรับ (const auto & [key, ถูกละเว้น]: items)
{
    itemKeys.push_back (คีย์);
}

ฉันไม่คิดว่าstd::ignoreจะใช้ ca ในการผูกโครงสร้างด้วยวิธีนี้ ฉันพบข้อผิดพลาดในการรวบรวม มันควรจะเพียงพอที่จะเพียงแค่ใช้ตัวแปรปกติเช่นignoredที่ไม่ได้ใช้
jb

1
@jb ขอบคุณ แน่นอนstd::ignoreมีไว้สำหรับใช้กับstd::tieแต่ไม่ได้มีการผูกโครงสร้าง ฉันอัพเดตรหัสแล้ว
Madiyar

9

ฉันคิดว่า BOOST_FOREACH ที่นำเสนอข้างต้นนั้นดีและสะอาดอย่างไรก็ตามมีตัวเลือกอื่นที่ใช้ BOOST เช่นกัน

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

std::map<int, int> m;
std::vector<int> keys;

using namespace boost::lambda;

transform(      m.begin(), 
                m.end(), 
                back_inserter(keys), 
                bind( &std::map<int,int>::value_type::first, _1 ) 
          );

copy( keys.begin(), keys.end(), std::ostream_iterator<int>(std::cout, "\n") );

โดยส่วนตัวฉันไม่คิดว่าวิธีนี้จะสะอาดเหมือนกับวิธี BOOST_FOREACH ในกรณีนี้ แต่การเพิ่ม :: lambda สามารถทำความสะอาดได้ในกรณีอื่น ๆ


7

นอกจากนี้หากคุณมี Boost ให้ใช้ transform_iterator เพื่อหลีกเลี่ยงการทำสำเนาคีย์ชั่วคราว



5

คุณสามารถใช้ boost boost :: transform_iterator transform_iterator ช่วยให้คุณสามารถแปลงค่าที่วนซ้ำตัวอย่างเช่นในกรณีของเราเมื่อคุณต้องการจัดการกับคีย์เท่านั้นไม่ใช่ค่า ดูhttp://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example


5

นี่เป็นเทมเพลตฟังก์ชั่นที่ดีที่ใช้เวทย์มนตร์ C ++ 11 ทำงานทั้ง std :: map, std :: unordered_map:

template<template <typename...> class MAP, class KEY, class VALUE>
std::vector<KEY>
keys(const MAP<KEY, VALUE>& map)
{
    std::vector<KEY> result;
    result.reserve(map.size());
    for(const auto& it : map){
        result.emplace_back(it.first);
    }
    return result;
}

ตรวจสอบที่นี่: http://ideone.com/lYBzpL


4

วิธีแก้ปัญหา STL ที่ไม่ใช่ sgi และ non-boost ที่ดีที่สุดคือการขยายแผนที่ :: ตัววนซ้ำเช่น:

template<class map_type>
class key_iterator : public map_type::iterator
{
public:
    typedef typename map_type::iterator map_iterator;
    typedef typename map_iterator::value_type::first_type key_type;

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;

    key_type& operator *()
    {
        return map_type::iterator::operator*().first;
    }
};

// helpers to create iterators easier:
template<class map_type>
key_iterator<map_type> key_begin(map_type& m)
{
    return key_iterator<map_type>(m.begin());
}
template<class map_type>
key_iterator<map_type> key_end(map_type& m)
{
    return key_iterator<map_type>(m.end());
}

แล้วใช้พวกเขาเช่นนั้น:

        map<string,int> test;
        test["one"] = 1;
        test["two"] = 2;

        vector<string> keys;

//      // method one
//      key_iterator<map<string,int> > kb(test.begin());
//      key_iterator<map<string,int> > ke(test.end());
//      keys.insert(keys.begin(), kb, ke);

//      // method two
//      keys.insert(keys.begin(),
//           key_iterator<map<string,int> >(test.begin()),
//           key_iterator<map<string,int> >(test.end()));

        // method three (with helpers)
        keys.insert(keys.begin(), key_begin(test), key_end(test));

        string one = keys[0];

1
ฉันจะปล่อยให้ผู้อ่านสร้างตัวคั่น const_iterator และย้อนกลับตัววนซ้ำหาก / เมื่อจำเป็น
Marius

-1

พร้อมตัวอย่างแผนที่ปรมาณู

#include <iostream>
#include <map>
#include <vector> 
#include <atomic>

using namespace std;

typedef std::atomic<std::uint32_t> atomic_uint32_t;
typedef std::map<int, atomic_uint32_t> atomic_map_t;

int main()
{
    atomic_map_t m;

    m[4] = 456;
    m[2] = 45678;

    vector<int> v;
    for(map<int,atomic_uint32_t>::iterator it = m.begin(); it != m.end(); ++it) {
      v.push_back(it->second);
      cout << it->first << " "<<it->second<<"\n";
    }

    return 0;
}

-2

คล้ายกับตัวอย่างเล็กน้อยที่นี่เล็กน้อยจากstd::mapมุมมองการใช้งานง่ายขึ้น

template<class KEY, class VALUE>
std::vector<KEY> getKeys(const std::map<KEY, VALUE>& map)
{
    std::vector<KEY> keys(map.size());
    for (const auto& it : map)
        keys.push_back(it.first);
    return keys;
}

ใช้แบบนี้:

auto keys = getKeys(yourMap);

2
เฮ้ฉันรู้ว่าคำตอบนี้เก่า แต่ก็ผิด การเริ่มต้นกับขนาดmap.size()หมายถึงการคืนขนาดเวกเตอร์เป็นสองเท่า โปรดแก้ไขเพื่อบันทึกคนอื่นปวดหัว :(
THC

-3

(ฉันสงสัยอยู่เสมอว่าทำไม std :: map ไม่รวมฟังก์ชั่นสมาชิกเพื่อให้เราทำเช่นนั้น)

เพราะมันไม่สามารถทำได้ดีกว่าที่คุณทำได้ หากการใช้งานของเมธอดจะไม่ดีกว่าการใช้งานฟังก์ชั่นฟรีโดยทั่วไปแล้วคุณไม่ควรเขียนวิธีการ; คุณควรเขียนฟังก์ชั่นฟรี

มันยังไม่ชัดเจนในทันทีว่าทำไมมันมีประโยชน์อยู่ดี


8
มีเหตุผลอื่นนอกเหนือจากประสิทธิภาพสำหรับห้องสมุดที่จะให้วิธีการเช่นฟังก์ชั่น "รวมแบตเตอรี่" และ API ที่รวมอยู่ด้วยกัน แม้ว่าจะยอมรับกันแล้วคำเหล่านั้นไม่ได้อธิบาย STL โดยเฉพาะ :) ไม่ชัดเจนว่าทำไมมันมีประโยชน์ - จริงเหรอ? ฉันคิดว่ามันชัดเจนว่าทำไมการแสดงรายการคีย์ที่มีอยู่เป็นสิ่งที่มีประโยชน์ที่สามารถทำกับแผนที่ / dict: ขึ้นอยู่กับสิ่งที่คุณใช้งาน
andybuckley

4
โดยเหตุผลนี้เรามี should't เพราะมันสามารถนำมาใช้เป็นempty() size() == 0
gd1

1
สิ่งที่ @ gd1 พูด แม้ว่าจะไม่ควรมีฟังก์ชั่นการทำงานซ้ำซ้อนในชั้นเรียนมาก แต่การยืนยันว่าศูนย์เป็นศูนย์ไม่ใช่ความคิดที่ดี IMO - อย่างน้อยก็จนกว่า C ++ จะช่วยให้เรา "อวยพร" ฟังก์ชั่นฟรีเป็นวิธีการ
einpoklum

1
ในรุ่นเก่าของ C ++ มีคอนเทนเนอร์ที่ว่างเปล่า () และขนาด () อาจมีการรับประกันประสิทธิภาพที่แตกต่างกันและฉันคิดว่าข้อมูลจำเพาะหลวมพอที่จะอนุญาตสิ่งนี้ (โดยเฉพาะรายการที่เชื่อมโยงที่เสนอเวลาต่อเนื่องคงที่ ()) . ด้วยเหตุนี้การแยกชิ้นส่วนจึงเหมาะสม ฉันไม่คิดว่าสิ่งนี้จะได้รับอนุญาตอีกต่อไป
DrPizza

ฉันเห็นด้วย. C ++ ถือว่าstd::map<T,U>เป็นคอนเทนเนอร์คู่ ใน Python การdictกระทำเช่นเดียวกับคีย์เมื่อทำซ้ำ แต่ให้คุณพูดd.items()เพื่อให้ได้พฤติกรรม C ++ d.values()งูใหญ่นอกจากนี้ยังมี std::map<T,U>แน่นอนสามารถให้keys()และvalues()วิธีการที่ส่งกลับวัตถุที่มีbegin()และend()ที่ให้ตัวทำซ้ำมากกว่าคีย์และค่า
Ben
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.