Tüm anahtarları (veya değerleri) bir std :: haritasından nasıl alıp bir vektöre nasıl koyabilirim?


246

Bu, çıkmamın olası yollarından biri:

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

Tabii ki, başka bir işlev RetrieveValues tanımlayarak tüm değerleri haritadan da alabiliriz .

Bunu kolayca başarmanın başka bir yolu var mı? (Her zaman std :: map'in bizim için neden bir üye işlevi içermediğini merak ediyorum.)


10
çözümün en iyisi ...
linello

4
Bu t eklemek istiyorum sadece düşünüyorum keys.reserve(m.size());.
Galik

Yanıtlar:


176

Çözümünüz işe yarayacak olsa da, diğer programcılarınızın beceri seviyesine bağlı olarak okumak zor olabilir. Ayrıca, işlevselliği çağrı sitesinden uzaklaştırır. Bu da bakımı biraz daha zorlaştırabilir.

Amacınız anahtarları bir vektöre almak mı yoksa cout için onları yazdırmak mı olduğundan emin değilim. Böyle bir şey deneyebilirsiniz:

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

Veya daha da basit, Boost kullanıyorsanız:

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

Şahsen, daha az yazım olduğu ve ne yaptığı hakkında çok açık olduğu için BOOST_FOREACH sürümünü seviyorum.


1
Google aramamdan sonra buraya geri döneceğim rakamlar. Sevgilerim tercih ettiğim cevap :)
mpen

4
@Jere - Aslında çalıştınız mı BOOST_FOREACH? Burada önerdiğiniz kod tamamen yanlış
Manuel

2
@Jamie - bu başka bir yol, ancak destek virgülleri, tür virgül içeriyorsa BOOST_FOREACH öncesinde değişkeni ve türünü belirtmeyi gösterir. Ayrıca bunu tanımlamayı gösterirler. Yani, kafam karıştı, kodumda sorun nedir?
Jere.Jones

17
Merakla, yeniden boyutlandırma tahsisini önlemek için vektörü yeniden boyutlandırmak mantıklı değil mi?
Alan

2
v.reserve(m.size())Aktarım sırasında vektörün yeniden boyutlandırılmasını önlemek için yapmayı unutmayın .
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
Güzel. Unut gitsin it = ...begin(); it != ...end. Tabii ki en güzel olurdu std :: harita bu vektör döndüren bir yöntem anahtarları () olan ...
masterxilo

2
@BenHymers: Bana öyle geliyor ki, bu cevap C ++ 11'in C ++ haline gelmesinden answered Mar 13 '12 at 22:33birkaç ay sonra verildi .
Sebastian Mach

37
@BenHymers ama şimdi soruyu okuyan herkes için kullanışlıdır, yani SO sadece bununla ilgilidir - sadece askere değil, diğer herkese de yardımcı olur.
Luchian Grigore

9
for (auto & imap) daha hassastır çünkü kopyalama işlemi yoktur.
HelloWorld

2
@StudentT, daha iyisi for(auto const & imap : mapints),.
cp.engr

61

Bu amaçla bir takviye aralığı adaptörü vardır :

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

Değerleri çıkarmak için benzer bir map_values ​​aralık bağdaştırıcısı vardır.


1
Ne yazık ki, boost::adaptorsBoost 1.43 kadar mevcut değil gibi görünüyor . Debian'ın (Squeeze) şu anki kararlı sürümü yalnızca Boost 1.42
Mickaël Le Baillif

2
Ne yazık. Boost 1.42, Squeeze'den 2,5 yıl önce Şubat 2010'da piyasaya sürüldü.
Alastair

Bu noktada, Squeeze Updates ve backport repo Boost 1.44 sunmalı mı?
Luis Machuca

hangi yükseltme başlığı tanımlanır?
James Wierzba

1
Bağlantılı doco'ya bakınız, burada tanımlanmıştırboost/range/adaptor/map.hpp
Alastair

46

C ++ 0x bize başka, mükemmel bir çözüm sundu:

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
Bence bu konuda mükemmel bir şey yok. std :: vektör <int> tuşları; keys.reserve (m_Inputs.size ()); for (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } Şifreli dönüşümden çok daha iyidir. Performans açısından bile. Bu daha iyi.
Jagannath

5
Karşılaştırılabilir performans istiyorsanız anahtarların boyutunu da burada ayırabilirsiniz. for döngüsünden kaçınmak istiyorsanız dönüşümü kullanın.
DanDan

4
sadece eklemek istiyorum - kullanabilirsiniz [] (const auto & pair)
ivan.ukr

@ ivan.ukr Hangi derleyiciyi kullanıyorsunuz? Bu sözdizimine burada izin verilmiyor: 'const auto': bir parametre 'auto' içeren bir türe sahip olamaz
Nisan'da Gobe

4
@ ivan.ukr otomatik parametre lambda içinde c ++ 14
roalz

16

@ DanDan'ın cevabı, C ++ 11 kullanarak:

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

ve kullanırken (@ ivan.ukr tarafından belirtildiği gibi) ++ 14 biz yerine decltype(map_in)::value_typeile auto.


5
keys.reserve(map_in.size());Verimlilik için ekleyebilirsiniz .
Galik

Dönüşüm yöntemi aslında for-loop daha fazla kod alır buluyorum.
user1633272

const türün arkasına konabilir! Neredeyse unutuyordum.
Zhang


10

Çözümünüz iyi ancak bunu yapmak için bir yineleyici kullanabilirsiniz:

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 çözümüne dayalı, ancak c ++ 17'de:

std :: map <int, int> öğeleri;
std :: vektör <int> itemKeys;

için (const auto & [anahtar, yoksayıldı]: öğeler)
{
    itemKeys.push_back (anahtar);
}

std::ignoreCa'nın bu şekilde yapılandırılmış ciltlemelerde kullanılabileceğini düşünmüyorum . Derleme hatası alıyorum. Sadece düzenli bir değişken kullanmak ignoredyeterlidir, örneğin kullanılmaz.
jb

1
@jb Teşekkürler. Gerçekten de, yapısal bağlarla birlikte std::ignorekullanılmak üzere tasarlanmıştır std::tie. Kodumu güncelledim.
Madiyar

9

Yukarıda sunulan BOOST_FOREACH güzel ve temiz olduğunu düşünüyorum, ancak BOOST da kullanarak başka bir seçenek var.

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

Şahsen, bu yaklaşımın bu durumda BOOST_FOREACH yaklaşımı kadar temiz olduğunu düşünmüyorum, ancak boost :: lambda diğer durumlarda gerçekten temiz olabilir.


7

Ayrıca, Boost'unuz varsa, anahtarların geçici bir kopyasını yapmaktan kaçınmak için transform_iterator kullanın.


7

Bir c ++ 11'in biti:

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


5

Her iki std :: map, std :: unordered_map için çalışan, C ++ 11 sihir kullanan güzel bir işlev şablonu:

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

Buradan kontrol edin: http://ideone.com/lYBzpL


4

En iyi sgi olmayan, desteklenmeyen STL çözümü haritayı genişletmektir: yineleyici şöyle:

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

ve sonra bunları şu şekilde kullanın:

        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
Ayrıca const_iterator oluşturmak ve gerekirse / gerekirse yineleyiciler oluşturmak için okuyucuya bırakacağım.
Marius

-1

Atomik harita örneği ile

#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

Buradaki örneklerden birine biraz benzer, std::mapkullanım açısından basitleştirilmiştir .

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

Bunun gibi kullanın:

auto keys = getKeys(yourMap);

2
Hey, bu cevabın eski olduğunu biliyorum ama aynı zamanda yanlış. Boyutla başlatma map.size(), vektör boyutu geri dönüşünün iki katı anlamına gelir. Baş ağrısını kurtarmak için lütfen onarın :(
thc

-3

(Her zaman std :: map'in bizim için neden bir üye işlevi içermediğini merak ediyorum.)

Çünkü bunu yapabileceğinden daha iyi yapamaz. Bir yöntemin uygulanması bir serbest işlevin uygulamasından daha üstün olmayacaksa, genel olarak bir yöntem yazmamalısınız; ücretsiz bir işlev yazmalısınız.

Niçin faydalı olduğu hemen belli değil.


8
Bir kitaplığın "piller dahil" işlevselliği ve tutarlı, kapsüllenmiş bir API gibi bir yöntem sağlama verimliliği dışında başka nedenler de vardır. Kuşkusuz bu terimlerin hiçbiri STL'yi özellikle iyi tarif etmese de :) Re. neden yararlı olduğunu net değil - gerçekten? Mevcut anahtarları listelemenin bir harita / dikte ile yapabilmek için neden yararlı bir şey olduğunu düşünüyorum: bunun için ne kullandığınıza bağlı.
andybuckley

4
Bu akıl yürütmemiz gerekmemelidir empty()çünkü bu şekilde uygulanabilir size() == 0.
gd1

1
@ Gd1 ne dedi. Bir sınıfta çok fazla işlevsel artıklık olmamasına rağmen, kesinlikle sıfırda ısrar etmek iyi bir fikir değildir IMO - en azından C ++ serbest fonksiyonları yöntemlere "kutsamaya" izin verene kadar.
einpoklum

1
C ++ 'ın eski sürümlerinde boş () ve size () makul olarak farklı performans garantilerine sahip olabilecek kaplar vardı ve spesifikasyonun buna izin verecek kadar gevşek olduğunu düşünüyorum (özellikle, sabit zamanlı ekleme () sunan bağlantılı listeler) . Bu nedenle, onları ayırmak mantıklıydı. Ancak bu tutarsızlığa artık izin verildiğini düşünmüyorum.
DrPizza

Katılıyorum. C ++ std::map<T,U>çiftler için bir kap görevi görür. Python'da, dictyinelendiğinde anahtarları gibi davranır, ancak d.items()C ++ davranışını almanızı söyler . Python da sağlar d.values(). std::map<T,U>kesinlikle bir nesneye sahip olan ve anahtarlar ve değerler üzerinde yineleyiciler sağlayan bir keys()ve values()yöntemi sağlayabilir. begin()end()
Ben
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.