Làm cách nào để truy xuất tất cả các khóa (hoặc giá trị) từ std :: map và đặt chúng vào một vectơ?


246

Đây là một trong những cách có thể tôi đi ra:

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

Tất nhiên, chúng ta cũng có thể truy xuất tất cả các giá trị từ bản đồ bằng cách xác định một functor khác RetrieveValues .

Có cách nào khác để đạt được điều này một cách dễ dàng? (Tôi luôn tự hỏi tại sao std :: map không bao gồm chức năng thành viên để chúng tôi làm như vậy.)


10
giải pháp của bạn là tốt nhất ...
linello

4
Chỉ nghĩ rằng tôi sẽ thêm điều này là keys.reserve(m.size());.
Galik

Câu trả lời:


176

Mặc dù giải pháp của bạn nên hoạt động, nó có thể khó đọc tùy thuộc vào mức độ kỹ năng của các lập trình viên đồng nghiệp của bạn. Ngoài ra, nó di chuyển chức năng ra khỏi trang web cuộc gọi. Mà có thể làm cho việc bảo trì khó khăn hơn một chút.

Tôi không chắc mục tiêu của bạn là đưa các phím vào một vectơ hay in chúng ra cout để tôi thực hiện cả hai. Bạn có thể thử một cái gì đó như thế này:

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

Hoặc thậm chí đơn giản hơn, nếu bạn đang sử dụng 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";
}

Cá nhân, tôi thích phiên bản BOOST_FOREACH vì ít gõ hơn và nó rất rõ ràng về những gì nó đang làm.


1
Đi số liệu tôi sẽ kết thúc ở đây sau khi tìm kiếm Google của tôi. Bạn là câu trả lời tôi thích :)
mpen

4
@ Jere - Bạn đã thực sự làm việc với BOOST_FOREACH? Mã bạn đề xuất ở đây là hoàn toàn sai
Manuel

2
@Jamie - đó là một cách khác, nhưng các tài liệu tăng cường hiển thị chỉ định biến và loại của nó trước BOOST_FOREACH nếu loại có dấu phẩy. Họ cũng cho thấy typedefing nó. Vì vậy, tôi bối rối, có gì sai với mã của tôi?
Jere.Jones

17
Tò mò, sẽ hợp lý hơn khi chỉ định vectơ để ngăn chặn thay đổi kích thước?
Alan

2
Đừng quên làm gì v.reserve(m.size())để tránh thay đổi kích thước vector trong quá trình chuyể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
Đẹp. Quên đi it = ...begin(); it != ...end. Tất nhiên, Nicest sẽ là std :: map có các khóa phương thức () trả về vectơ đó ...
masterxilo

2
@BenHymers: Dường như với tôi câu trả lời này đã được đưa ra vào answered Mar 13 '12 at 22:33, đó là vài tháng sau khi C ++ 11 trở thành C ++.
Sebastian Mach

37
@BenHymers nhưng nó được sử dụng cho bất cứ ai đang đọc câu hỏi bây giờ, đó là tất cả những gì SO - không chỉ giúp người hỏi, mà tất cả những người khác.
Luchian Grigore

9
for (auto & imap) chính xác hơn vì không có thao tác sao chép.
HelloWorld

2
@StudentT, tốt hơn nữa , for(auto const & imap : mapints).
cp.engr

61

Có một bộ chuyển đổi phạm vi tăng cho mục đích này:

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

Có một bộ điều hợp phạm vi map_values ​​tương tự để trích xuất các giá trị.


1
Thật không may, có vẻ như boost::adaptorskhông có sẵn cho đến khi Boost 1.43. Bản phát hành ổn định hiện tại của Debian (Bóp) chỉ cung cấp Boost 1.42
Mickaël Le Baillif

2
Đó là một điều đáng tiếc. Boost 1.42 được phát hành vào tháng 2 năm 2010, hơn 2,5 năm trước khi Squeeze.
Alastair

Tại thời điểm này, không nên Bóp cập nhật và hoặc repo backport sẽ cung cấp Boost 1.44?
Luis Machuca

tiêu đề tăng được xác định trong?
James Wierzba

1
Xem tài liệu được liên kết, nó được xác định trongboost/range/adaptor/map.hpp
Alastair

46

C ++ 0x đã cho chúng ta một giải pháp tuyệt vời hơn nữa:

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
Theo quan điểm của tôi không có gì xuất sắc về nó. phím std :: vector <int>; key.reserve (m_Inputs.size ()); for (auto keyValue: m_Inputs) {Keys.push_back (keyValue.first); } Tốt hơn nhiều so với biến đổi mật mã. Ngay cả về hiệu suất. Cái này thì tốt hơn.
Jagannath

5
Bạn cũng có thể dự trữ kích thước của các phím ở đây nếu bạn muốn hiệu suất tương đương. sử dụng biến đổi nếu bạn muốn tránh một vòng lặp for.
DanDan

4
chỉ muốn thêm - có thể sử dụng [] (const auto & cặp)
ivan.ukr

@ ivan.ukr bạn đang sử dụng trình biên dịch nào? Cú pháp này không được phép ở đây: 'const auto &': một tham số không thể có loại chứa 'auto'
Gobe

4
Tham số tự động @ ivan.ukr trong lambda là c ++ 14
roalz

16

Câu trả lời của @ DanDan, sử dụng C ++ 11 là:

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

và sử dụng C ++ 14 (như được ghi chú bởi @ ivan.ukr) chúng ta có thể thay thế decltype(map_in)::value_typebằng auto.


5
Bạn có thể thêm keys.reserve(map_in.size());cho hiệu quả.
Galik

Tôi thấy phương thức biến đổi thực sự cần nhiều mã hơn for-loop.
dùng1633272

const có thể được đặt phía sau các loại! Tôi gần như quên mất điều đó.
Zhang


10

Giải pháp của bạn là tốt nhưng bạn có thể sử dụng một trình vòng lặp để làm điều đó:

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

Dựa trên giải pháp @ rusty-park, nhưng trong c ++ 17:

std :: map <int, int> mục;
std :: vector <int> itemKeys;

for (const auto & [key, bị bỏ qua]: mục)
{
    itemKeys.push_back (khóa);
}

Tôi không nghĩ std::ignoreca được sử dụng trong các ràng buộc có cấu trúc theo cách này. Tôi đang nhận được một lỗi biên dịch. Nó chỉ đủ để sử dụng một biến thông thường, ví dụ như ignoredđơn giản là không được sử dụng.
jb

1
@jb Cảm ơn. Thật vậy, std::ignoređược thiết kế để sử dụng với std::tienhưng không phải với các ràng buộc cấu trúc. Tôi đã cập nhật mã của mình.
Madiyar

9

Tôi nghĩ rằng BOOST_FOREACH được trình bày ở trên là tốt và sạch sẽ, tuy nhiên, có một tùy chọn khác sử dụng BOOST là tốt.

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

Cá nhân, tôi không nghĩ cách tiếp cận này sạch như cách tiếp cận BOOST_FOREACH trong trường hợp này, nhưng boost :: lambda có thể thực sự sạch trong các trường hợp khác.


7

Ngoài ra, nếu bạn có Boost, hãy sử dụng Transform_iterator để tránh tạo một bản sao tạm thời của các phím.


7

Bit của một c ++ 11 mất:

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

Đây là một mẫu hàm đẹp sử dụng phép thuật C ++ 11, hoạt động cho cả 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;
}

Kiểm tra nó ở đây: http://ideone.com/lYBzpL


4

Giải pháp STL không tăng tốc, không tăng tốc tốt nhất là mở rộng map :: iterator như vậy:

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

và sau đó sử dụng chúng như vậy:

        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
Tôi cũng sẽ để nó cho người đọc để tạo const_iterator và đảo ngược nếu nếu cần.
Marius

-1

Với ví dụ bản đồ nguyên tử

#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

Hơi giống với một trong những ví dụ ở đây, được đơn giản hóa từ std::mapquan điểm sử dụng.

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

Sử dụng như thế này:

auto keys = getKeys(yourMap);

2
Này, tôi biết câu trả lời này đã cũ nhưng nó cũng sai. Khởi tạo với kích thước map.size()có nghĩa là gấp đôi lợi nhuận của vectơ. Vui lòng sửa để cứu người khác đau đầu :(
thc

-3

(Tôi luôn tự hỏi tại sao std :: map không bao gồm chức năng thành viên để chúng tôi làm như vậy.)

Bởi vì nó không thể làm điều đó tốt hơn bạn có thể làm. Nếu việc triển khai một phương thức sẽ không vượt trội so với triển khai chức năng miễn phí thì nói chung, bạn không nên viết một phương thức; bạn nên viết một hàm miễn phí

Nó cũng không ngay lập tức rõ ràng tại sao nó hữu ích.


8
Có nhiều lý do khác ngoài hiệu quả để thư viện cung cấp một phương thức, chẳng hạn như chức năng "bao gồm pin" và API đóng gói, mạch lạc. Mặc dù phải thừa nhận rằng cả hai điều khoản này không mô tả STL đặc biệt tốt :) Re. không rõ tại sao nó hữu ích - thực sự? Tôi nghĩ khá rõ ràng tại sao việc liệt kê các khóa khả dụng là một điều hữu ích để có thể thực hiện với bản đồ / dict: nó phụ thuộc vào việc bạn đang sử dụng nó để làm gì.
andybuckley

4
Theo lý do này, chúng ta không nên có empty()vì nó có thể được thực hiện như size() == 0.
gd1

1
Những gì @ gd1 nói. Mặc dù không có nhiều dư thừa chức năng trong một lớp, nhưng nhấn mạnh vào số 0 hoàn toàn không phải là một ý tưởng hay IMO - ít nhất là cho đến khi C ++ cho phép chúng ta "ban phước" các hàm miễn phí vào các phương thức.
einpoklum 2/2/2016

1
Trong các phiên bản cũ hơn của C ++, có các thùng chứa trống () và kích thước () có thể có các đảm bảo hiệu suất khác nhau một cách hợp lý và tôi nghĩ rằng thông số kỹ thuật này đủ lỏng để cho phép điều này (cụ thể, các danh sách được liên kết cung cấp mối nối thời gian không đổi ()) . Như vậy, tách chúng có ý nghĩa. Tuy nhiên, tôi không nghĩ rằng sự khác biệt này được cho phép nữa.
DrPizza

Tôi đồng ý. C ++ coi std::map<T,U>như một thùng chứa các cặp. Trong Python, một dicthành động giống như các khóa của nó khi lặp đi lặp lại, nhưng cho phép bạn nói d.items()để có được hành vi C ++. Python cũng cung cấp d.values(). std::map<T,U>chắc chắn có thể cung cấp một keys()values()phương thức trả về một đối tượng có begin()end()cung cấp các trình vòng lặp qua các khóa và giá trị.
Ben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.