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).

piekarnik
źródło
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.

Jere.Jones
źródło
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);
Juan
źródło
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.

Alastair
źródło
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;});
DanDan
źródło
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.

James Hirschorn
źródło
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
12

SGI STL ma rozszerzenie o nazwie select1st. Szkoda, że ​​nie ma go w standardowym STL!

Chris Jester-Young
źródło
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
}
Brian R. Bondy
źródło
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);
}
Madiyar
źródło
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.

paxos1977
źródło
7

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

Marcelo Cantos
źródło
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;
}
Zardzewiałe parki
źródło
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

Clemens Sielaff
źródło
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];
Marius
źródło
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;
}
Deniz Babat
źródło
-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);
TarmoPikaro
źródło
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.

DrPizza
źródło
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