Bagaimana cara mengambil semua kunci (atau nilai) dari std :: map dan memasukkannya ke dalam vektor?

246

Ini adalah salah satu cara yang mungkin saya lakukan:

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

Tentu saja, kita juga dapat mengambil semua nilai dari peta dengan mendefinisikan functor lain RetrieveValues .

Apakah ada cara lain untuk mencapai ini dengan mudah? (Saya selalu bertanya-tanya mengapa std :: map tidak menyertakan fungsi anggota untuk kita lakukan.)

Owen
sumber
10
solusi Anda adalah yang terbaik ...
linello
4
Satu-satunya pikir saya akan menambahkan ini keys.reserve(m.size());.
Galik

Jawaban:

176

Walaupun solusi Anda harusnya berhasil, bisa jadi sulit untuk membaca tergantung pada tingkat keterampilan rekan programmer Anda. Selain itu, ini memindahkan fungsionalitas dari situs panggilan. Yang bisa membuat perawatan sedikit lebih sulit.

Saya tidak yakin apakah tujuan Anda adalah memasukkan kunci ke vektor atau mencetaknya ke cout, jadi saya melakukan keduanya. Anda dapat mencoba sesuatu seperti ini:

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

Atau bahkan lebih sederhana, jika Anda menggunakan 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";
}

Secara pribadi, saya suka versi BOOST_FOREACH karena mengetik kurang dan sangat eksplisit tentang apa yang dilakukannya.

Jere.Jones
sumber
1
Go figure, saya akan kembali ke sini setelah pencarian Google saya. Milik Anda adalah jawaban yang saya sukai :)
mpen
4
@Jere - Apakah Anda benar-benar bekerja dengan BOOST_FOREACH? Kode yang Anda usulkan di sini benar-benar salah
Manuel
2
@ Jamie - itu adalah cara lain, tetapi dokumen boost menunjukkan menentukan variabel dan jenisnya sebelum BOOST_FOREACH jika jenisnya mengandung koma. Mereka juga menunjukkan mengetik itu. Jadi, saya bingung, ada apa dengan kode saya?
Jere.Jones
17
Penasaran, tidakkah masuk akal untuk mempresisi vektor untuk mencegah alokasi ukuran?
Alan
2
Jangan lupa lakukan v.reserve(m.size())untuk menghindari pengubahan ukuran vektor selama transfer.
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);
Juan
sumber
4
Bagus. Lupakan it = ...begin(); it != ...end. Tentu saja yang terbaik adalah std :: map yang memiliki kunci metode () mengembalikan vektor itu ...
masterxilo
2
@ BenHymers: Bagi saya jawaban ini diberikan pada answered Mar 13 '12 at 22:33, yaitu beberapa bulan setelah C ++ 11 menjadi C ++.
Sebastian Mach
37
@BenHymers tetapi berguna bagi siapa saja yang membaca pertanyaan sekarang, yang adalah tentang SO - tidak hanya membantu penanya, tetapi semua orang.
Luchian Grigore
9
untuk (auto & imap) lebih tepat karena tidak ada operasi penyalinan.
HelloWorld
2
@StudentT, lebih baik lagi for(auto const & imap : mapints),.
cp.engr
61

Ada adaptor jangkauan jelajah untuk tujuan ini:

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

Ada adaptor rentang map_values ​​serupa untuk mengekstraksi nilai.

Alastair
sumber
1
Sayangnya, sepertinya boost::adaptorstidak tersedia sampai Boost 1.43. Rilis stabil Debian saat ini (Squeeze) hanya menawarkan Boost 1.42
Mickaël Le Baillif
2
Sangat disayangkan. Boost 1.42 dirilis pada Februari 2010, lebih dari 2,5 tahun sebelum Squeeze.
Alastair
Pada titik ini, bukankah Squeeze Updates dan atau repo backports menawarkan Boost 1.44?
Luis Machuca
yang meningkatkan header yang didefinisikan dalam
James Wierzba
1
Lihat dokumen yang ditautkan, didefinisikan diboost/range/adaptor/map.hpp
Alastair
46

C ++ 0x telah memberi kami solusi lebih lanjut, sangat baik:

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;});
DanDan
sumber
22
Dalam pandangan saya tidak ada yang luar biasa tentang hal itu. std :: vector <int> kunci; keys.reserve (m_Inputs.size ()); untuk (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } Jauh lebih baik daripada transformasi samar. Bahkan dalam hal kinerja. Yang ini lebih baik.
Jagannath
5
Anda dapat memesan ukuran kunci di sini juga jika Anda menginginkan kinerja yang sebanding. gunakan transformasi jika Anda ingin menghindari for for.
DanDan
4
hanya ingin menambahkan - bisa menggunakan [] (const & pasangan otomatis)
ivan.ukr
@ ivan.ukr kompiler apa yang Anda gunakan? Sintaks ini tidak diizinkan di sini: 'const auto &': parameter tidak dapat memiliki tipe yang mengandung 'auto'
Gobe
4
@ ivan.ukr parameter otomatis dalam lambda adalah c ++ 14
roalz
16

@ Jawaban DanDan, menggunakan C ++ 11 adalah:

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

dan menggunakan C ++ 14 (seperti dicatat oleh @ ivan.ukr) kita dapat mengganti decltype(map_in)::value_typedengan auto.

James Hirschorn
sumber
5
Anda dapat menambah keys.reserve(map_in.size());efisiensi.
Galik
Saya menemukan metode transformasi sebenarnya membutuhkan lebih banyak kode daripada untuk-loop.
user1633272
const dapat diletakkan di belakang tipenya! Saya hampir melupakannya.
Zhang
12

SGI STL memiliki ekstensi yang disebut select1st. Sayang sekali tidak dalam STL standar!

Chris Jester-Young
sumber
10

Solusi Anda baik-baik saja tetapi Anda dapat menggunakan iterator untuk melakukannya:

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
}
Brian R. Bondy
sumber
10

Berbasis pada solusi @ rusty-taman, tetapi dalam c ++ 17:

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

for (const auto & [key, ignored]: items)
{
    itemKeys.push_back (kunci);
}
Madiyar
sumber
Saya tidak berpikir std::ignoreca dapat digunakan dalam binding terstruktur dengan cara ini. Saya mendapatkan kesalahan kompilasi. Seharusnya cukup dengan hanya menggunakan variabel biasa mis. ignoredYang tidak digunakan.
jb
1
@ jb, terima kasih. Memang, std::ignoredimaksudkan untuk digunakan dengan std::tietetapi tidak dengan binding struktural. Saya telah memperbarui kode saya.
Madiyar
9

Saya pikir BOOST_FOREACH yang disajikan di atas bagus dan bersih, namun, ada opsi lain menggunakan BOOST juga.

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

Secara pribadi, saya tidak berpikir pendekatan ini sebersih pendekatan BOOST_FOREACH dalam kasus ini, tetapi boost :: lambda dapat benar-benar bersih dalam kasus lain.

paxos1977
sumber
7

Juga, jika Anda memiliki Peningkatan, gunakan transform_iterator untuk menghindari membuat salinan kunci sementara.

Marcelo Cantos
sumber
7

Sedikit dari c ++ 11 take:

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto & kvp : items)
{
    itemKeys.emplace_back(kvp.first);
    std::cout << kvp.first << std::endl;
}
Taman Berkarat
sumber
5

Inilah templat fungsi yang bagus menggunakan C ++ 11 magic, yang bekerja untuk 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;
}

Lihat di sini: http://ideone.com/lYBzpL

Clemens Sielaff
sumber
4

Solusi STL non-sgi, non-boost terbaik adalah memperluas peta :: iterator seperti:

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

dan kemudian gunakan seperti itu:

        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];
Marius
sumber
1
Saya akan menyerahkannya kepada pembaca untuk juga membuat const_iterator dan membalikkan iterators jika diperlukan.
Marius
-1

Dengan contoh peta atom

#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;
}
Deniz Babat
sumber
-2

Sedikit mirip dengan salah satu contoh di sini, disederhanakan dari std::mapperspektif penggunaan.

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

Gunakan seperti ini:

auto keys = getKeys(yourMap);
TarmoPikaro
sumber
2
Hei, saya tahu jawaban ini sudah tua tetapi juga salah. Menginisialisasi dengan ukuran map.size()berarti menggandakan pengembalian ukuran vektor. Silakan perbaiki untuk menyelamatkan orang lain sakit kepala :(
thc
-3

(Saya selalu bertanya-tanya mengapa std :: map tidak menyertakan fungsi anggota untuk kita lakukan.)

Karena itu tidak bisa melakukannya lebih baik daripada Anda bisa melakukannya. Jika implementasi metode tidak akan lebih unggul daripada implementasi fungsi bebas maka secara umum Anda tidak boleh menulis metode; Anda harus menulis fungsi gratis.

Juga tidak jelas mengapa itu berguna.

DrPizza
sumber
8
Ada alasan lain selain efisiensi bagi perpustakaan untuk menyediakan metode, seperti fungsi "termasuk baterai", dan API enkapsulasi yang koheren. Meskipun harus diakui tidak satu pun dari istilah-istilah tersebut menggambarkan STL sangat baik :) Re. tidak jelas mengapa ini berguna - sungguh? Saya pikir itu cukup jelas mengapa daftar kunci yang tersedia adalah hal yang berguna untuk dapat dilakukan dengan peta / dict: itu tergantung pada apa yang Anda gunakan untuk itu.
andybuckley
4
Dengan alasan ini, kita seharusnya tidak memilikinya empty()karena dapat diimplementasikan sebagai size() == 0.
gd1
1
Apa yang dikatakan @ gd1. Meskipun seharusnya tidak ada banyak redundansi fungsional di kelas, bersikeras nol sama sekali bukan ide yang baik IMO - setidaknya sampai C ++ memungkinkan kita untuk "memberkati" fungsi bebas ke dalam metode.
einpoklum
1
Dalam versi yang lebih lama dari C ++ ada wadah yang kosong () dan ukuran () dapat cukup memiliki jaminan kinerja yang berbeda, dan saya pikir spek itu cukup longgar untuk mengizinkan ini (khususnya, daftar terkait yang menawarkan sambungan waktu konstan ()) . Dengan demikian, memisahkan mereka masuk akal. Saya pikir perbedaan ini tidak diizinkan lagi.
DrPizza
Saya setuju. C ++ memperlakukan std::map<T,U>sebagai wadah pasangan. Dalam Python, dicttindakan seperti kuncinya ketika diulangi, tetapi memungkinkan Anda mengatakan d.items()untuk mendapatkan perilaku C ++. Python juga menyediakan d.values(). std::map<T,U>tentu saja dapat menyediakan keys()dan values()metode yang mengembalikan objek yang memiliki begin()dan end()yang menyediakan iterator atas kunci dan nilai.
Ben