Xác định xem bản đồ có chứa giá trị cho khóa không?


256

Cách tốt nhất để xác định xem bản đồ STL có chứa giá trị cho một khóa đã cho không là gì?

#include <map>

using namespace std;

struct Bar
{
    int i;
};

int main()
{
    map<int, Bar> m;
    Bar b = {0};
    Bar b1 = {1};

    m[0] = b;
    m[1] = b1;

    //Bar b2 = m[2];
    map<int, Bar>::iterator iter = m.find(2);
    Bar b3 = iter->second;

}

Kiểm tra điều này trong một trình gỡ lỗi, có vẻ như iterchỉ là dữ liệu rác.

Nếu tôi bỏ ghi chú dòng này:

Bar b2 = m[2]

Các chương trình gỡ rối mà b2{i = 0}. (Tôi đoán điều đó có nghĩa là việc sử dụng một chỉ mục không xác định sẽ trả về một cấu trúc với tất cả các giá trị trống / chưa được khởi tạo?)

Cả hai phương pháp này đều rất tuyệt Điều tôi thực sự thích là một giao diện như thế này:

bool getValue(int key, Bar& out)
{
    if (map contains value for key)
    {
        out = map[key];
        return true;
    }
    return false;
}

Có một cái gì đó dọc theo những dòng này tồn tại?


Câu trả lời:


274

Có một cái gì đó dọc theo những dòng này tồn tại?

Không. Với lớp bản đồ stl, bạn sử dụng ::find()để tìm kiếm bản đồ và so sánh trình lặp được trả về vớistd::map::end()

vì thế

map<int,Bar>::iterator it = m.find('2');
Bar b3;
if(it != m.end())
{
   //element found;
   b3 = it->second;
}

Rõ ràng là bạn có thể viết getValue()thói quen của riêng mình nếu bạn muốn (cũng như trong C ++, không có lý do gì để sử dụng out), nhưng tôi sẽ nghi ngờ rằng một khi bạn sử dụng xong std::map::find()bạn sẽ không muốn lãng phí thời gian của mình.

Ngoài ra mã của bạn hơi sai:

m.find('2');sẽ tìm kiếm bản đồ cho một giá trị đó là '2'. IIRC trình biên dịch C ++ sẽ ngầm chuyển đổi '2' thành int, dẫn đến giá trị số cho mã ASCII cho '2' không phải là điều bạn muốn.

Vì keytype của bạn trong ví dụ này là intbạn muốn tìm kiếm như thế này:m.find(2);


7
Làm sao vậy findcho thấy ý định tốt hơn nhiều so với countkhông. Hơn nữa, countkhông trả lại hàng. Nếu bạn đọc câu hỏi của OP, anh ấy muốn kiểm tra sự tồn tại trả lại phần tử. findlàm điều đó countkhông làm.
Alan

if (m.count (khóa)) b3 = m [key]; // hoặc bất cứ điều gì
pconnell 16/07/13

62
Tôi luôn tò mò về loại cỏ dại nào đã hút những người thiết kế toàn bộ API stl.
Bẫy

2
Alan Tôi phải đồng ý với @dynamic về điều này, phải xác định một trình vòng lặp và sau đó so sánh nó với kết thúc không phải là một cách tự nhiên để nói rằng một cái gì đó không tồn tại. Tôi có vẻ đơn giản hơn nhiều khi nói rằng một yếu tố nào đó xuất hiện ít nhất một lần trong bản đồ này. Đó là những gì tính.
PiersyP

1
@Claudiu C ++ 20 thêm vào đó.
pooya13

330

Miễn là bản đồ không phải là đa điểm, một trong những cách thanh lịch nhất là sử dụng phương pháp đếm

if (m.count(key))
    // key exists

Số lượng sẽ là 1 nếu phần tử thực sự có mặt trên bản đồ.


22
Điều này sẽ không kiểm tra tất cả các khóa ngay cả khi nó đã tìm thấy một khóa? Điều đó có thể trở nên đắt đỏ nhanh chóng ...
mmdanziger

35
Nó sẽ chỉ đếm nhiều hơn một khóa nếu được sử dụng trên nhiều chế độ.
Andrew Prock

14
@mmdanziger Không, nó sẽ không đắt: cplusplus.com/reference/map/map/count Count có kích thước logarit.
jgyou

28
Chìa khóa tồn tại, và sau đó là gì? Tại thời điểm đó, bạn thường muốn nhận được giá trị cho nó, trả tiền cho một tìm kiếm khác (ví dụ: sử dụng operator[]). findcung cấp cho bạn TryGetValuengữ nghĩa của .NET , gần như luôn luôn là những gì bạn (và cụ thể là OP) muốn.
Ohad Schneider

2
@serine Hiểu rồi. Lưu ý rằng trong trường hợp khóa bị thiếu khi phát hành, hành vi sẽ khác, vì ánh xạ [khóa] sẽ trả về giá trị phần tử được xây dựng mặc định.
Ohad Schneider

53

Nó đã tồn tại với chỉ tìm không theo cú pháp chính xác.

if (m.find(2) == m.end() )
{
    // key 2 doesn't exist
}

Nếu bạn muốn truy cập giá trị nếu nó tồn tại, bạn có thể làm:

map<int, Bar>::iterator iter = m.find(2);
if (iter != m.end() )
{
    // key 2 exists, do something with iter->second (the value)
}

Với C ++ 0x và auto, cú pháp đơn giản hơn:

auto iter = m.find(2);
if (iter != m.end() )
{
    // key 2 exists, do something with iter->second (the value)
}

Tôi khuyên bạn nên làm quen với nó hơn là cố gắng đưa ra một cơ chế mới để đơn giản hóa nó. Bạn có thể cắt giảm một chút mã, nhưng hãy xem xét chi phí để làm điều đó. Bây giờ bạn đã giới thiệu một chức năng mới mà những người quen thuộc với C ++ sẽ không thể nhận ra.

Nếu bạn muốn thực hiện điều này bằng bất chấp những cảnh báo này, thì:

template <class Key, class Value, class Comparator, class Alloc>
bool getValue(const std::map<Key, Value, Comparator, Alloc>& my_map, int key, Value& out)
{
    typename std::map<Key, Value, Comparator, Alloc>::const_iterator it = my_map.find(key);
    if (it != my_map.end() )
    {
        out = it->second;
        return true;
    }
    return false;
}

38

Tôi chỉ nhận thấy rằng với C ++ 20 , chúng ta sẽ có

bool std::map::contains( const Key& key ) const;

Điều đó sẽ trả về true nếu map giữ một phần tử có khóa key.


Cuối cùng một câu trả lời nói về chức năng này! (C ++ 20)
Samuel Rasquinha

Cuối cùng? Cảm ơn, nhưng nó đã gần 2 tuổi! ;-)
kebs

7

amap.findtrả về amap::endkhi nó không tìm thấy thứ bạn đang tìm kiếm - bạn phải kiểm tra cái đó.


4

Kiểm tra giá trị trả lại findchống lại end.

map<int, Bar>::iterator it = m.find('2');
if ( m.end() != it ) { 
  // contains
  ...
}

1

Bạn có thể tạo hàm getValue của mình với đoạn mã sau:

bool getValue(const std::map<int, Bar>& input, int key, Bar& out)
{
   std::map<int, Bar>::iterator foundIter = input.find(key);
   if (foundIter != input.end())
   {
      out = foundIter->second;
      return true;
   }
   return false;
}

Tôi tin rằng dòng 6 nên làout = foundIter->second
Dithermaster

Tôi đã sửa câu trả lời của Kip để hiển thị chính xác out = foundIter->secondthay vìout = *foundIter
netjeff

1

Để tóm tắt ngắn gọn một số câu trả lời khác:

Nếu bạn chưa sử dụng C ++ 20, bạn có thể viết mapContainsKeychức năng của riêng mình :

bool mapContainsKey(std::map<int, int>& map, int key)
{
  if (map.find(key) == map.end()) return false;
  return true;
}

Nếu bạn muốn tránh nhiều quá tải cho mapvs unordered_mapvà khoá và giá trị khác nhau, bạn có thể làm điều này một templatechức năng.

Nếu bạn đang sử dụng C++ 20hoặc sau này, sẽ có một containschức năng tích hợp:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}

-1

Nếu bạn muốn xác định xem một khóa có trong bản đồ hay không, bạn có thể sử dụng hàm thành viên find () hoặc Count () của bản đồ. Hàm find được sử dụng ở đây trong ví dụ trả về iterator thành phần tử hoặc map :: end nếu không. Trong trường hợp đếm, số đếm trả về 1 nếu tìm thấy, nếu không, nó sẽ trả về 0 (hoặc nếu không).

if(phone.count(key))
{ //key found
}
else
{//key not found
}

for(int i=0;i<v.size();i++){
    phoneMap::iterator itr=phone.find(v[i]);//I have used a vector in this example to check through map you cal receive a value using at() e.g: map.at(key);
    if(itr!=phone.end())
        cout<<v[i]<<"="<<itr->second<<endl;
    else
        cout<<"Not found"<<endl;
}

-1

Boost multindex có thể được sử dụng cho giải pháp thích hợp. Giải pháp sau đây không phải là một lựa chọn tốt nhất nhưng có thể hữu ích trong một số trường hợp người dùng đang gán giá trị mặc định như 0 hoặc NULL khi khởi tạo và muốn kiểm tra xem giá trị đã được sửa đổi chưa.

Ex.
< int , string >
< string , int > 
< string , string > 

consider < string , string >
mymap["1st"]="first";
mymap["second"]="";
for (std::map<string,string>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
{
       if ( it->second =="" ) 
            continue;
}
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.