Cách tìm nếu khóa đã cho tồn tại trong C ++ std :: map


450

Tôi đang cố kiểm tra xem một khóa đã cho có trong bản đồ hay không và không thể làm điều đó:

typedef map<string,string>::iterator mi;
map<string, string> m;
m.insert(make_pair("f","++--"));
pair<mi,mi> p = m.equal_range("f");//I'm not sure if equal_range does what I want
cout << p.first;//I'm getting error here

Vì vậy, làm thế nào tôi có thể in những gì trong p?


std::pair<iterator,bool> insert( const value_type& value );Bool nó trả về là gì? Nó có nói, nếu chìa khóa đã có mặt hay chưa?
krithikaGopalakris Nam

Câu trả lời:


691

Sử dụng map::find

if ( m.find("f") == m.end() ) {
  // not found
} else {
  // found
}

105
Nếu bạn chỉ muốn kiểm tra xem một khóa nào đó có tồn tại hay không, có lẽ bạn nên sử dụngmap::count
tomsmeding

10
@tomsmeding Chỉ có một khóa duy nhất trong std :: map. Vì vậy, số đếm sẽ là 0 hoặc 1. Cái này có hiệu quả hơn cái kia không?
goelakash

34
@goelakash hầu như không; nó chỉ counttrả về một intlúc findtrả về toàn bộ vòng lặp. Bạn lưu cấu trúc của iterator :) Rõ ràng, nếu sau đó bạn sẽ sử dụng giá trị nếu nó tồn tại, hãy sử dụng find và lưu trữ kết quả của nó.
tomsmeding

9
@tomsmeding Nếu bạn đang sử dụng multimap, bạn phải xem qua toàn bộ container. Trong trường hợp đó, find () có thể nhanh hơn.
Trevor Hickey

11
Đối với những người đang tìm kiếm tốc độ: countfindgần như giống nhau về tốc độ khi sử dụng bản đồ yêu cầu các phím duy nhất. (1) Nếu bạn không cần các yếu tố để duy trì một trật tự cụ thể, hãy sử dụng std :: unordered_map , có các tra cứu gần như không đổi và có thể rất có lợi khi lưu trữ nhiều hơn một vài cặp. (2) Nếu bạn muốn sử dụng giá trị nếu nó tồn tại, hãy lưu trữ kết quả của :: find và sử dụng trình lặp để ngăn 2 lần tra cứu:auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
cdgraham

305

Để kiểm tra xem một khóa cụ thể trong bản đồ có tồn tại hay không, hãy sử dụng countchức năng thành viên theo một trong các cách sau:

m.count(key) > 0
m.count(key) == 1
m.count(key) != 0

Các tài liệu cho map::findbiết: "Một hàm thành viên,map::count có thể được sử dụng để chỉ kiểm tra xem một phím đặc biệt tồn tại."

Các tài liệu hướng dẫn chomap::count biết: "Bởi vì tất cả các yếu tố trong một container đồ là duy nhất, chức năng chỉ có thể trở lại 1 (nếu phần tử được tìm thấy) hoặc không có (nếu không)."

Để truy xuất giá trị từ bản đồ thông qua khóa mà bạn biết là tồn tại, hãy sử dụng map :: at :

value = m.at(key)

Không giống như map :: toán tử [] , map::atsẽ không tạo khóa mới trong bản đồ nếu khóa được chỉ định không tồn tại.


33
Nếu bạn sẽ thực hiện cả hai thao tác, hãy kiểm tra xem nó có tồn tại không và sau đó làm gì đó với nó. Sử dụng findthay thế. Các secondthuộc tính của iterator được trả về bởi findcó thể được sử dụng lấy giá trị của phím. Nếu bạn sử dụng countsau đó athoặc operator[]bạn đang thực hiện hai hoạt động khi bạn có thể đã sử dụng chỉ có một.
OdraEncoding

1
Bạn không cần phải làm> 0, == 1 hoặc! = 0; đó là kiểm tra chính xác C ++ thực hiện trong câu lệnh if (condition! = 0), vì vậy bạn chỉ có thể sử dụngif(m.count(key))
jv110

6
@ jv110 Trình biên dịch Microsoft C ++ đưa ra cảnh báo khi nó gặp phải một cast từ intđến bool. Mặc dù có các trình biên dịch C ++ khác không đưa ra cảnh báo tương tự, tôi thích sử dụng so sánh rõ ràng để làm rõ ý định và tăng cường khả năng đọc. Lưu ý rằng các ngôn ngữ khác như C # cấm chuyển đổi ngầm định như vậy để ngăn khả năng đưa ra các lỗi lập trình tinh vi.
DavidRR

độ phức tạp của thời gian là gì? Có phải chỉ là hoạt động O (1)?
Mazeryt

1
@Mazeryt Cho rằng chúng ta đang nói về một lớp trong thư viện chuẩn C ++, tôi chắc chắn sẽ cho là như vậy. Đối với một cuộc thảo luận không biết ngôn ngữ về câu hỏi của bạn, hãy xem Bảng băm có thể thực sự là O (1) không? .
DavidRR

47

C ++ 20 cho chúng ta std::map::containslàm điều đó.

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> example = {{1, "One"}, {2, "Two"}, 
                                     {3, "Three"}, {42, "Don\'t Panic!!!"}};

    if(example.contains(42)) {
        std::cout << "Found\n";
    } else {
        std::cout << "Not found\n";
    }
}

34
Tôi đoán tôi sẽ nói điều đó: Cuối cùng.
Erik Campobadal

2
Về thời gian .....
Ridhuvarshan

39

Bạn có thể sử dụng .find():

map<string,string>::iterator i = m.find("f");

if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }

15
m.find == m.end() // not found 

Nếu bạn muốn sử dụng API khác, hãy tìm m.count(c)>0

 if (m.count("f")>0)
      cout << " is an element of m.\n";
    else 
      cout << " is not an element of m.\n";

12

Tôi nghĩ bạn muốn map::find. Nếu m.find("f")bằng m.end(), thì không tìm thấy khóa. Mặt khác, find trả về một iterator chỉ vào phần tử được tìm thấy.

Lỗi là do p.firstiterator, không hoạt động để chèn luồng. Thay đổi dòng cuối cùng của bạn thành cout << (p.first)->first;. plà một cặp iterator, p.firstlà một iterator, p.first->firstlà chuỗi khóa.

Bản đồ chỉ có thể có một yếu tố cho một khóa nhất định, vì vậy equal_rangekhông hữu ích lắm. Nó được xác định cho bản đồ, bởi vì nó được xác định cho tất cả các thùng chứa kết hợp, nhưng nó thú vị hơn nhiều đối với đa chế độ.


Trên thực tế, vì là một cặp trình lặp cho bản đồ, nên nó phải là "cout << p.first-> trước;"
stefaanv

Tôi đã sửa câu trả lời của mình, cảm ơn. Đó là những gì tôi nhận được khi không biên dịch mã của mình. Và bạn đã đúng (trong một bình luận đã xóa) về việc kiểm tra tính hợp lệ, nhưng tôi chỉ cố gắng giải thích lý do tại sao anh ấy không thể in p.first, và không phải vì nó không hợp lệ - chúng tôi biết "f" sẽ được tìm thấy. Vì tôi không khuyên bạn nên sử dụng Equ_range, tôi sẽ không hiển thị mã kiểm tra lỗi cho điều đó.
Steve Jessop

Ồ, bạn thực sự đang quét SO. Tôi chỉ thêm nó cho đầy đủ, bởi vì quan điểm của bạn đã rõ ràng. Tôi đã thêm kiểm tra tính hợp lệ vào câu trả lời trước của tôi, nhưng phản hồi của bạn đã đánh bại tôi, vì vậy tôi đã xóa nó, vì dù sao nó cũng không thêm nhiều như vậy, như bạn đã đề cập.
stefaanv

Vâng, tôi chỉ thấy nó bởi vì bình luận của bạn xuất hiện khi tôi đăng bài của tôi.
Steve Jessop

12

C++17đơn giản hóa điều này hơn một chút với một If statement with initializer. Bằng cách này bạn có thể có bánh của bạn và ăn nó quá.

if ( auto it{ m.find( "key" ) }; it != std::end( m ) ) 
{
    // Use `structured binding` to get the key
    // and value.
    auto[ key, value ] { *it };

    // Grab either the key or value stored in the pair.
    // The key is stored in the 'first' variable and
    // the 'value' is stored in the second.
    auto mkey{ it->first };
    auto mvalue{ it->second };

    // That or just grab the entire pair pointed
    // to by the iterator.
    auto pair{ *it };
} 
else 
{
   // Key was not found..
}

4
map<string, string> m;

kiểm tra khóa có tồn tại hay không và số lần trả về xảy ra (0/1 trên bản đồ):

int num = m.count("f");  
if (num>0) {    
    //found   
} else {  
    // not found  
}

kiểm tra khóa có tồn tại hay không và trả về iterator:

map<string,string>::iterator mi = m.find("f");  
if(mi != m.end()) {  
    //found  
    //do something to mi.  
} else {  
    // not found  
}  

trong câu hỏi của bạn, lỗi do xấu operator<<quá tải, vì p.firstmap<string, string>, bạn không thể in nó ra. thử cái này:

if(p.first != p.second) {
    cout << p.first->first << " " << p.first->second << endl;
}

1
Bạn có một lỗi đánh máy. Thay đổi "cout" thành "đếm"
Rivka

1
Và lỗi đánh máy đó thực sự có thể khiến ai đó bỏ đi, vì nó coutcó thể có nghĩa gì đó rất khác so vớicount
modulitos

4
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
    return (container.find(key) != std::end(container));
}

Tất nhiên, nếu bạn muốn có được fancier, bạn luôn có thể tạo ra một hàm cũng lấy một hàm tìm thấy và một hàm không tìm thấy, đại loại như thế này:

template <typename T, typename Key, typename FoundFunction, typename NotFoundFunction>
void find_and_execute(const T& container, const Key& key, FoundFunction found_function, NotFoundFunction not_found_function)
{
    auto& it = container.find(key);
    if (it != std::end(container))
    {
        found_function(key, it->second);
    }
    else
    {
        not_found_function(key);
    }
}

Và sử dụng nó như thế này:

    std::map<int, int> some_map;
    find_and_execute(some_map, 1,
        [](int key, int value){ std::cout << "key " << key << " found, value: " << value << std::endl; },
        [](int key){ std::cout << "key " << key << " not found" << std::endl; });

Nhược điểm của việc này là xuất hiện một cái tên hay, "find_and_execute" thật khó xử và tôi không thể nghĩ ra bất cứ điều gì tốt hơn ngoài đỉnh đầu của mình ...


3

Hãy cẩn thận trong việc so sánh kết quả tìm kiếm với kết thúc như đối với bản đồ 'm' vì tất cả các câu trả lời đã được thực hiện ở trên map :: iterator i = m.find ("f");

 if (i == m.end())
 {
 }
 else
 {
 }  

bạn không nên thử và thực hiện bất kỳ thao tác nào như in khóa hoặc giá trị bằng iterator i nếu nó bằng m.end () nếu không nó sẽ dẫn đến lỗi phân đoạn.


0

So sánh mã của std :: map :: find và std :: map :: Count, tôi muốn nói rằng lần đầu tiên có thể mang lại một số lợi thế về hiệu suất:

const_iterator find(const key_type& _Keyval) const
    {   // find an element in nonmutable sequence that matches _Keyval
    const_iterator _Where = lower_bound(_Keyval); // Here one looks only for lower bound
    return (_Where == end()
        || _DEBUG_LT_PRED(this->_Getcomp(),
            _Keyval, this->_Key(_Where._Mynode()))
                ? end() : _Where);
    }

size_type count(const key_type& _Keyval) const
    {   // count all elements that match _Keyval
    _Paircc _Ans = equal_range(_Keyval); // Here both lower and upper bounds are to be found, which is presumably slower.
    size_type _Num = 0;
    _Distance(_Ans.first, _Ans.second, _Num);
    return (_Num);
    }

0

Tôi biết câu hỏi này đã có một số câu trả lời hay nhưng tôi nghĩ giải pháp của tôi đáng để chia sẻ.

Nó hoạt động cho cả hai std::mapstd::vector<std::pair<T, U>>có sẵn từ C ++ 11.

template <typename ForwardIterator, typename Key>
bool contains_key(ForwardIterator first, ForwardIterator last, Key const key) {
    using ValueType = typename std::iterator_traits<ForwardIterator>::value_type;

    auto search_result = std::find_if(
        first, last,
        [&key](ValueType const& item) {
            return item.first == key;
        }
    );

    if (search_result == last) {
        return false;
    } else {
        return true;
    }
}

-5

Nếu bạn muốn so sánh cặp bản đồ, bạn có thể sử dụng phương pháp này:

typedef map<double, double> TestMap;
TestMap testMap;
pair<map<double,double>::iterator,bool> controlMapValues;

controlMapValues= testMap.insert(std::pair<double,double>(x,y));
if (controlMapValues.second == false )
{
    TestMap::iterator it;
    it = testMap.find(x);

    if (it->second == y)
    {
        cout<<"Given value is already exist in Map"<<endl;
    }
}

Đây là một kỹ thuật hữu ích.


Là người mới bắt đầu với lập trình C ++, tôi thực sự tò mò tại sao câu trả lời này lại bị hạ thấp. Tại sao câu trả lời này không phổ biến?
gromit190

3
@ gromit190 vì nó sử dụng toàn bộ cấu trúc dữ liệu khác để xem khóa có tồn tại khi std :: map có khả năng này không. Điều này cũng đòi hỏi phải đồng bộ hóa giữa hai cấu trúc dữ liệu vốn là một sự phụ thuộc mà không ai muốn đối phó.
Lambage

-5
map <int , char>::iterator itr;
    for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
    {
        if (itr->second == 'c')
        {
            cout<<itr->first<<endl;
        }
    }

3
Xin hãy giải thích về mã của bạn. Một đoạn trích mà không có bất kỳ lời giải thích nào có xu hướng không hữu ích trong thời gian dài.
iBug
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.