Jak pobrać wszystkie klucze (lub wartości) ze std :: map i umieścić je w wektorze?


246

To jeden z możliwych sposobów, w jaki wychodzę:

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

Oczywiście możemy również pobrać wszystkie wartości z mapy, definiując kolejny funktor RetrieveValues .

Czy jest jakiś inny sposób na osiągnięcie tego łatwo? (Zawsze zastanawiam się, dlaczego std :: map nie zawiera dla nas funkcji członka).


10
twoje rozwiązanie jest najlepsze ...
linello

4
Jedyne, co chciałbym dodać, to to keys.reserve(m.size());.
Galik

Odpowiedzi:


176

Chociaż twoje rozwiązanie powinno działać, może być trudne do odczytania, w zależności od poziomu umiejętności innych programistów. Dodatkowo przenosi funkcjonalność z dala od strony połączeń. Co może utrudnić konserwację.

Nie jestem pewien, czy twoim celem jest wprowadzenie kluczy do wektora lub wydrukowanie ich, aby zrobić to, więc robię oba. Możesz spróbować czegoś takiego:

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

Lub nawet prościej, jeśli używasz wzmocnienia:

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

Osobiście podoba mi się wersja BOOST_FOREACH, ponieważ jest mniej pisania i jest bardzo wyraźna na temat tego, co robi.


1
Idź, dane, że skończę tutaj po mojej wyszukiwarce Google. Twoja odpowiedź brzmi : wolę :)
mpen

4
@Jere - Czy faktycznie pracowałeś BOOST_FOREACH? Proponowany tutaj kod jest całkowicie niepoprawny
Manuel

2
@Jamie - to inny sposób, ale dokumenty doładowania pokazują określenie zmiennej i jej typu przed BOOST_FOREACH, jeśli typ zawiera przecinek. Pokazują też pisząc na maszynie. Więc jestem zdezorientowany, co jest nie tak z moim kodem?
Jere.Jones

17
Ciekawe, czy nie ma sensu ustawiać wektora, aby zapobiec alokacji zmiany rozmiaru?
Alan

2
Nie zapomnij zrobić, v.reserve(m.size())aby uniknąć zmiany rozmiaru wektora podczas przesyłania.
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
Miły. Zapomnij o it = ...begin(); it != ...end. Najładniejszy byłby oczywiście std :: map posiadający metodę keys () zwracającą ten wektor ...
masterxilo

2
@BenHymers: Wydaje mi się, że odpowiedź ta została udzielona w answered Mar 13 '12 at 22:33kilka miesięcy po tym, jak C ++ 11 stało się C ++.
Sebastian Mach

37
@BenHymers, ale przydaje się każdemu, kto teraz czyta pytanie, na tym właśnie polega SO - nie tylko pomagając pytającemu, ale wszystkim innym.
Luchian Grigore

9
Funkcja (auto i imap) jest bardziej precyzyjna, ponieważ nie wykonuje operacji kopiowania.
HelloWorld,

2
@StudentT jeszcze lepiej for(auto const & imap : mapints).
cp.engr

61

Jest to adapter zakres impuls do tego celu:

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

Istnieje podobny adapter zakresu map_values ​​do wyodrębniania wartości.


1
Niestety wygląda na to, że boost::adaptorsnie jest dostępny do momentu wzmocnienia 1.43. Obecne stabilne wydanie Debiana (Squeeze) oferuje tylko Boost 1.42
Mickaël Le Baillif

2
Szkoda. Zwiększenie 1.42 zostało wydane w lutym 2010 roku, ponad 2,5 roku przed Squeeze.
Alastair

Czy w tym momencie Squeeze Updates i / lub repozytorium backportów nie powinny oferować Boost 1.44?
Luis Machuca

w którym nagłówku doładowania jest zdefiniowane?
James Wierzba

1
Zobacz powiązany dokument, jest on zdefiniowany wboost/range/adaptor/map.hpp
Alastair

46

C ++ 0x dał nam kolejne doskonałe rozwiązanie:

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
Moim zdaniem nie ma w tym nic doskonałego. std :: vector <int> klucze; keys.reserve (m_Inputs.size ()); for (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } Jest znacznie lepszy niż tajemnicza transformacja. Nawet pod względem wydajności. Ten jest lepszy.
Jagannath

5
Tutaj możesz również zarezerwować rozmiar kluczy, jeśli chcesz mieć porównywalną wydajność. użyj transformacji, jeśli chcesz uniknąć pętli for.
DanDan

4
po prostu chcę dodać - można użyć [] (const auto & pair)
ivan.ukr

@ ivan.ukr jakiego kompilatora używasz? Ta składnia jest tutaj niedozwolona: „const auto &”: parametr nie może mieć typu zawierającego „auto”
Gobe

4
@ ivan.ukr auto parametrem w lambda jest c ++ 14
roalz 13.10.17

16

Odpowiedź DanDana przy użyciu C ++ 11 brzmi:

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

i przy użyciu C ++ 14 (jak wskazano przez @ ivan.ukr) może zastąpić decltype(map_in)::value_typez auto.


5
Możesz dodać keys.reserve(map_in.size());do wydajności.
Galik

Uważam, że metoda transformacji zajmuje więcej kodu niż w pętli for.
user1633272,

const można umieścić za typem! Prawie o tym zapomniałem.
Zhang


10

Twoje rozwiązanie jest w porządku, ale możesz to zrobić za pomocą iteratora:

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

Oparty na rozwiązaniu @ rusty-parks, ale w c ++ 17:

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

for (const auto & [key, ignored]: items)
{
    itemKeys.push_back (klucz);
}

Nie sądzę, żeby std::ignoremożna było tego używać w powiązaniach strukturalnych. Otrzymuję błąd kompilacji. Wystarczy użyć zwykłej zmiennej, np. Po ignoredprostu się nie przyzwyczai.
jb

1
@jb Dzięki. Rzeczywiście, std::ignorejest przeznaczony do stosowania z, std::tieale nie z wiązaniami strukturalnymi. Zaktualizowałem swój kod.
Madiyar,

9

Myślę, że BOOST_FOREACH przedstawiony powyżej jest ładny i czysty, jednak istnieje również inna opcja korzystania z 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") );

Osobiście nie sądzę, aby to podejście było tak czyste jak podejście BOOST_FOREACH w tym przypadku, ale boost :: lambda może być naprawdę czyste w innych przypadkach.


7

Ponadto, jeśli masz Boost, użyj transform_iterator, aby uniknąć tworzenia tymczasowej kopii kluczy.


7

Trochę c ++ 11 wziąć:

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

Oto ładny szablon funkcji wykorzystujący magię C ++ 11, działający zarówno dla 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;
}

Sprawdź to tutaj: http://ideone.com/lYBzpL


4

Najlepszym rozwiązaniem STL innym niż sgi, nie doładowującym jest rozszerzenie map :: iterator w następujący sposób:

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

a następnie użyj ich w następujący sposób:

        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
Zostawię to czytelnikowi, aby również utworzył const_iterator i odwrócił iteratory, jeśli / w razie potrzeby.
Marius

-1

Z przykładem mapy atomowej

#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

Nieco podobny do jednego z przykładów tutaj, uproszczony z std::mapperspektywy użytkowania.

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

Użyj w ten sposób:

auto keys = getKeys(yourMap);

2
Hej, wiem, że ta odpowiedź jest stara, ale także błędna. Inicjowanie za map.size()pomocą rozmiaru oznacza dwukrotność zwrotu rozmiaru wektora. Proszę naprawić, aby zaoszczędzić komuś ból głowy :(
thc

-3

(Zawsze zastanawiam się, dlaczego std :: map nie zawiera dla nas funkcji członka).

Ponieważ nie może tego zrobić lepiej niż ty. Jeśli implementacja metody nie będzie lepsza niż implementacja wolnej funkcji, to na ogół nie powinieneś pisać metody; powinieneś napisać darmową funkcję.

Nie jest też od razu jasne, dlaczego jest to przydatne.


8
Istnieją inne powody niż efektywność biblioteki w celu zapewnienia metody, takie jak funkcjonalność „dołączone baterie” i spójny, enkapsulowany API. Chociaż wprawdzie żaden z tych terminów nie opisuje szczególnie dobrze STL :) Re. nie jest jasne, dlaczego jest to przydatne - naprawdę? Myślę, że to dość oczywiste, dlaczego lista dostępnych kluczy jest przydatna w przypadku mapy / dict: zależy to od tego, do czego ją używasz.
andybuckley

4
Zgodnie z tym rozumowaniem nie powinniśmy go mieć, empty()ponieważ można go zaimplementować jako size() == 0.
gd1

1
Co powiedział @ gd1. Chociaż w klasie nie powinno być dużo nadmiarowości funkcjonalnej, naleganie na absolutne zero nie jest dobrym pomysłem IMO - przynajmniej dopóki C ++ nie pozwoli nam „błogosławić” wolnych funkcji w metodach.
einpoklum

1
W starszych wersjach C ++ istniały kontenery, dla których puste () i rozmiar () mogły mieć rozsądnie różne gwarancje wydajności, i myślę, że specyfikacja była wystarczająco luźna, aby na to pozwolić (konkretnie, połączone listy, które oferowały splice w czasie stałym ()) . W związku z tym ich oddzielenie miało sens. Jednak nie sądzę, aby ta rozbieżność była już dozwolona.
DrPizza

Zgadzam się. C ++ traktuje std::map<T,U>jak kontener par. W Pythonie a dictdziała jak klucze po iteracji, ale pozwala powiedzieć, d.items()aby uzyskać zachowanie w C ++. Python zapewnia również d.values(). std::map<T,U>z pewnością może zapewnić metodę keys()i values(), która zwraca obiekt, który ma begin()i end()zapewnia iteratory nad kluczami i wartościami.
Ben
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.