Tentukan apakah peta berisi nilai untuk kunci?

256

Apa cara terbaik untuk menentukan apakah peta STL berisi nilai untuk kunci yang diberikan?

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

}

Memeriksa ini dalam debugger, sepertinya iterhanya data sampah.

Jika saya batalkan komentar pada baris ini:

Bar b2 = m[2]

Debugger menunjukkan bahwa b2adalah {i = 0}. (Saya menduga itu berarti bahwa menggunakan indeks yang tidak ditentukan akan mengembalikan struct dengan semua nilai kosong / tidak diinisialisasi?)

Tidak satu pun dari metode ini yang begitu hebat. Yang benar-benar saya suka adalah antarmuka seperti ini:

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

Apakah ada sesuatu di sepanjang garis ini ada?

Nick Heiner
sumber

Jawaban:

274

Apakah ada sesuatu di sepanjang garis ini ada?

Tidak. Dengan kelas peta stl, Anda menggunakan ::find()untuk mencari peta, dan membandingkan iterator yang dikembalikanstd::map::end()

begitu

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

Jelas Anda dapat menulis getValue()rutin sendiri jika Anda mau (juga di C ++, tidak ada alasan untuk menggunakan out), tetapi saya akan curiga bahwa begitu Anda terbiasa menggunakan std::map::find()Anda tidak akan mau membuang waktu Anda.

Juga kode Anda sedikit salah:

m.find('2');akan mencari peta untuk nilai kunci itu '2'. IIRC, kompiler C ++ secara implisit akan mengkonversi '2' ke int, yang menghasilkan nilai numerik untuk kode ASCII untuk '2' yang bukan yang Anda inginkan.

Karena jenis kunci Anda dalam contoh ini adalah intAnda ingin mencari seperti ini:m.find(2);

Alan
sumber
7
Bagaimana? findmenunjukkan niat jauh lebih baik daripada countmelakukannya. Terlebih lagi, counttidak mengembalikan item. Jika Anda membaca pertanyaan OP, dia ingin memeriksa keberadaannya, dan mengembalikan elemen tersebut. findapakah itu. counttidak.
Alan
if (m.count (key)) b3 = m [key]; // atau apa pun
pconnell
64
Saya selalu ingin tahu seperti apa gulma merokok orang-orang yang merancang seluruh stl API.
Trap
2
Alan saya harus setuju dengan @dynamic yang satu ini, harus mendefinisikan iterator dan kemudian membandingkannya dengan akhir bukanlah cara alami untuk mengatakan bahwa sesuatu tidak ada. Tampaknya jauh lebih mudah bagi saya untuk mengatakan bahwa elemen tertentu muncul setidaknya satu kali di peta ini. Yang penting dihitung.
PiersyP
1
@Claudiu C ++ 20 menambahkan hal itu.
pooya13
330

Selama peta bukan multimap, salah satu cara paling elegan adalah dengan menggunakan metode penghitungan

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

Hitungannya akan menjadi 1 jika elemen tersebut memang ada di peta.

PConnell
sumber
22
Bukankah ini akan memeriksa semua kunci bahkan jika sudah menemukan satu kunci? Itu bisa menjadi cepat mahal ...
mmdanziger
35
Ini hanya akan menghitung lebih dari satu kunci jika digunakan pada multimap.
Andrew Prock
14
@mmdanziger Tidak, ini tidak akan mahal: cplusplus.com/reference/map/map/count Count adalah ukuran logaritmik.
jgyou
28
Kuncinya ada, lalu apa? Pada titik itu Anda biasanya ingin mendapatkan nilai untuk itu, membayar untuk pencarian lain (misalnya menggunakan operator[]). findmemberi Anda TryGetValuesemantik NET. , yang hampir selalu apa yang Anda (dan khususnya OP) inginkan.
Ohad Schneider
2
@serine Dipahami. Perhatikan bahwa jika kunci tidak ada dalam rilis perilaku akan berbeda, karena peta [kunci] akan mengembalikan nilai elemen yang baru dibangun secara default.
Ohad Schneider
53

Itu sudah ada dengan find saja tidak dalam sintaks yang tepat.

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

Jika Anda ingin mengakses nilai jika ada, Anda dapat melakukan:

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

Dengan C ++ 0x dan otomatis, sintaksinya lebih sederhana:

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

Saya sarankan Anda terbiasa daripada mencoba untuk membuat mekanisme baru untuk menyederhanakannya. Anda mungkin dapat mengurangi sedikit kode, tetapi pertimbangkan biaya untuk melakukan itu. Sekarang Anda telah memperkenalkan fungsi baru yang tidak dapat dikenali oleh orang yang akrab dengan C ++.

Jika Anda tetap ingin menerapkan ini terlepas dari peringatan ini, maka:

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;
}
stinky472
sumber
38

Saya hanya memperhatikan bahwa dengan C ++ 20 , kita akan memilikinya

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

Itu akan kembali benar jika peta memegang elemen dengan kunci key.

kebs
sumber
Akhirnya jawaban yang berbicara tentang fungsi ini! (C ++ 20)
Samuel Rasquinha
Akhirnya? Terima kasih, tapi sudah hampir 2 tahun! ;-)
kebs
7

amap.findkembali amap::endketika tidak menemukan apa yang Anda cari - Anda seharusnya memeriksanya.

Alex Martelli
sumber
4

Periksa nilai balik findterhadap end.

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

Anda dapat membuat fungsi getValue Anda dengan kode berikut:

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;
}
Kip Streithorst
sumber
Saya percaya baris 6 seharusnyaout = foundIter->second
Dithermaster
Saya memperbaiki jawaban Kip untuk menunjukkan dengan benar out = foundIter->seconddaripadaout = *foundIter
netjeff
1

Untuk meringkas beberapa jawaban lain dengan ringkas:

Jika Anda belum menggunakan C ++ 20, Anda dapat menulis mapContainsKeyfungsi Anda sendiri :

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

Jika Anda ingin menghindari banyak kelebihan mapvs unordered_mapdan jenis kunci dan nilai yang berbeda, Anda bisa menjadikannya templatefungsi.

Jika Anda menggunakan C++ 20atau lebih baru, akan ada containsfungsi bawaan:

std::map<int, int> myMap;

// do stuff with myMap here

int key = 123;

if (myMap.contains(key))
{
  // stuff here
}
cdahms
sumber
-1

Jika Anda ingin menentukan apakah kunci ada di peta atau tidak, Anda dapat menggunakan fungsi find () atau count () anggota. Fungsi find yang digunakan di sini sebagai contoh mengembalikan iterator ke elemen atau map :: end sebaliknya. Dalam hal penghitungan, penghitungan pengembalian 1 jika ditemukan, jika tidak maka akan mengembalikan nol (atau sebaliknya).

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;
}
Prashant Shubham
sumber
-1

Boost multindex dapat digunakan untuk solusi yang tepat. Solusi berikut bukanlah pilihan terbaik tetapi mungkin berguna dalam beberapa kasus di mana pengguna menetapkan nilai default seperti 0 atau NULL pada inisialisasi dan ingin memeriksa apakah nilainya telah dimodifikasi.

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