จะค้นหาได้อย่างไรว่าคีย์ที่ระบุนั้นมีอยู่ใน C ++ std :: map


451

ฉันพยายามตรวจสอบว่ามีรหัสที่ระบุอยู่ในแผนที่หรือไม่และทำไม่ได้:

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

ดังนั้นฉันจะพิมพ์สิ่งที่อยู่ใน p ได้อย่างไร


std::pair<iterator,bool> insert( const value_type& value );บูลคืออะไรมันกลับมา? มันบอกหรือไม่ว่ากุญแจนั้นมีอยู่แล้วหรือไม่?
krithikaGopalakrisnan

คำตอบ:


692

ใช้ map::find

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

105
หากคุณเพียงต้องการตรวจสอบว่ามีคีย์บางคีย์อยู่หรือไม่คุณอาจใช้แทนmap::count
tomsmeding

10
@tomsmeding มีเพียงคีย์เดียวในแผนที่ std :: ดังนั้นการนับจะเป็น 0 หรือ 1 อย่างใดอย่างหนึ่งที่มีประสิทธิภาพมากกว่าที่อื่น ๆ ?
goelakash

34
@goelakash แทบจะไม่; มันเป็นเพียงแค่countคืนค่าintขณะที่findส่งคืนตัววนซ้ำทั้งหมด คุณบันทึกโครงสร้างของตัววนซ้ำ :) แน่นอนถ้าคุณหลังจากนั้นจะใช้ค่าถ้ามีอยู่ให้ใช้ find และเก็บผลลัพธ์
tomsmeding

9
@tomsmeding หากคุณใช้ Multimap คุณจะต้องตรวจสอบคอนเทนเนอร์ทั้งหมด ในกรณีนี้ find () อาจเร็วกว่า
เทรเวอร์ Hickey

11
สำหรับผู้ที่กำลังมองหาความเร็ว: countและfindเกือบจะเหมือนกันในเรื่องความเร็วเมื่อใช้แผนที่ที่ต้องใช้ปุ่มพิเศษ (1) หากคุณไม่ต้องการองค์ประกอบในการรักษาคำสั่งซื้อที่เฉพาะเจาะจงให้ใช้std :: unordered_mapซึ่งมีการค้นหาที่ใกล้เคียงคงที่และมีประโยชน์มากเมื่อเก็บมากกว่าสองสามคู่ (2) หากคุณต้องการใช้ค่าหากมีอยู่ให้เก็บผลลัพธ์ของ :: find และใช้ตัววนซ้ำเพื่อป้องกันการค้นหา 2 รายการ:auto it = m.find("f"); if (it != m.end()) {/*Use it->second*/}
cdgraham

305

ในการตรวจสอบว่ามีคีย์เฉพาะในแผนที่อยู่หรือไม่ให้ใช้countฟังก์ชันสมาชิกด้วยวิธีใดวิธีหนึ่งต่อไปนี้:

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

เอกสารสำหรับการmap::findพูดว่า: "ฟังก์ชั่นสมาชิกอีกคนหนึ่งmap::countที่สามารถนำมาใช้ในการตรวจสอบว่าเป็นเพียงแค่คีย์โดยเฉพาะอย่างยิ่งที่มีอยู่."

เอกสารสำหรับการmap::countพูดว่า: "เพราะทุกองค์ประกอบในภาชนะแผนที่จะไม่ซ้ำกันฟังก์ชั่นเท่านั้นที่สามารถกลับ 1 (ถ้ามีองค์ประกอบที่พบ) หรือศูนย์ (อื่น ๆ )."

ในการดึงค่าจากแผนที่ผ่านกุญแจที่คุณรู้ว่ามีอยู่ให้ใช้แผนที่ :: ที่ :

value = m.at(key)

ซึ่งแตกต่างจากแผนที่ :: ผู้ประกอบการ [] , map::atจะไม่สร้างคีย์ใหม่ในแผนที่ถ้าคีย์ที่ระบุไม่มีอยู่


33
หากคุณกำลังจะดำเนินการทั้งสองให้ตรวจสอบว่ามีอยู่แล้วทำอะไรกับมัน ใช้findแทน แอsecondททริบิวของตัววนซ้ำที่ส่งคืนโดยfindสามารถใช้เพื่อเรียกคืนค่าของคีย์ ถ้าคุณใช้countแล้วatหรือoperator[]คุณกำลังดำเนินการทั้งสองดำเนินการเมื่อคุณจะได้ใช้เพียงคนเดียว
OdraEncoded

1
คุณไม่จำเป็นต้องทำ> 0, == 1 หรือ! = 0; นี่คือการตรวจสอบ C ++ ที่แน่นอนในคำสั่ง if (เงื่อนไข! = 0) ดังนั้นคุณสามารถใช้if(m.count(key))
jv110

6
@ jv110 ของ Microsoft C ++ คอมไพเลอร์ปัญหาการแจ้งเตือนเมื่อพบการโยนจากไปint boolแม้ว่าจะมีคอมไพเลอร์ C ++ อื่น ๆ ที่ไม่มีคำเตือนที่คล้ายกัน แต่ฉันชอบใช้การเปรียบเทียบที่ชัดเจนเพื่อทำให้เจตนาชัดเจนและเพิ่มความสามารถในการอ่าน โปรดทราบว่าภาษาอื่นเช่น C # ห้ามการแปลงโดยนัยเพื่อป้องกันความเป็นไปได้ในการแนะนำข้อผิดพลาดการเขียนโปรแกรมที่ละเอียด
DavidRR

ความซับซ้อนของการนับเวลาคืออะไร? มันเป็นเพียงการดำเนินการ O (1)?
Mazeryt

1
@ Mazeryt เนื่องจากเรากำลังพูดถึงคลาสในไลบรารีมาตรฐาน C ++ ฉันจะถือว่าเป็นเช่นนั้น สำหรับการอภิปรายคำถามที่ไม่เชื่อเรื่องภาษาของคุณโปรดดูที่ตารางแฮชเป็น O (1) จริงหรือ .
DavidRR

47

C ++ 20ให้เราstd::map::containsทำเช่นนั้น

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

35
ฉันเดาว่าฉันจะบอกว่า: ในที่สุด
Erik Campobadal

2
ประมาณเวลา .....
Ridhuvarshan


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

หากคุณต้องการใช้ API อื่น ๆ ให้ลองค้นหา 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

map::findฉันคิดว่าคุณต้องการ หากm.find("f")เท่ากับm.end()แล้วจะไม่พบกุญแจ มิฉะนั้น find ส่งคืนตัววนซ้ำที่ชี้ไปที่องค์ประกอบที่พบ

ข้อผิดพลาดเป็นเพราะp.firstเป็นตัววนซ้ำซึ่งไม่ได้ผลสำหรับการแทรกแบบสตรีม cout << (p.first)->first;เปลี่ยนบรรทัดสุดท้ายของคุณเพื่อ pเป็นคู่ของตัววนซ้ำp.firstเป็นตัววนซ้ำp.first->firstสตริงสำคัญ

แผนที่สามารถมีองค์ประกอบหนึ่งรายการสำหรับคีย์ที่ระบุเท่านั้นจึงequal_rangeไม่มีประโยชน์มาก มันถูกกำหนดไว้สำหรับแผนที่เพราะมันถูกกำหนดไว้สำหรับคอนเทนเนอร์ที่เชื่อมโยงทั้งหมด แต่มันน่าสนใจกว่าสำหรับมัลติแม็พ


ที่จริงแล้วเนื่องจากเป็นตัววนซ้ำของแผนที่จึงควรเป็น "cout << p.first-> first;"
stefaanv

ฉันคงคำตอบแล้วขอบคุณ นั่นคือสิ่งที่ฉันได้รับจากการไม่คอมไพล์โค้ดของฉัน และคุณถูก (ในความคิดเห็นที่ถูกลบ) เกี่ยวกับการตรวจสอบความถูกต้อง แต่ฉันแค่พยายามอธิบายว่าทำไมเขาไม่สามารถพิมพ์ p.first ก่อนและไม่ใช่เพราะมันไม่ถูกต้อง - เรารู้ว่าจะพบ "f" เนื่องจากฉันไม่แนะนำให้ใช้ equal_range เลยฉันไม่ควรแสดงรหัสตรวจสอบข้อผิดพลาด
Steve Jessop

ว้าวคุณกำลังสแกน SO อยู่จริงๆ ฉันแค่เพิ่มมันเพื่อความสมบูรณ์เพราะประเด็นของคุณชัดเจน ฉันได้เพิ่มการตรวจสอบความถูกต้องในคำตอบก่อนหน้านี้ แต่คำตอบของคุณตีฉันฉันจึงลบมันเพราะมันไม่ได้เพิ่มมากไปกว่านั้นตามที่คุณพูดถึง
stefaanv

ใช่ฉันเห็น แต่เพียงความคิดเห็นของคุณปรากฏเมื่อฉันโพสต์ของฉัน
Steve Jessop

12

C++17If statement with initializerง่ายนี้มากขึ้นอีกนิดกับ วิธีนี้คุณสามารถมีเค้กของคุณและกินมันเกินไป

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;

มีรหัสตรวจสอบอยู่หรือไม่และส่งคืนจำนวนที่เกิดขึ้น (0/1 ในแผนที่):

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

ตรวจสอบรหัสที่มีอยู่หรือไม่และส่งคืนตัววนซ้ำ:

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

ในคำถามของคุณข้อผิดพลาดที่เกิดจากการที่ไม่ดีoperator<<เกินเพราะp.firstเป็นmap<string, string>คุณจะไม่สามารถพิมพ์ออกมา ลองนี้:

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

1
คุณพิมพ์ผิด เปลี่ยน "cout" เป็น "count"
Rivka

1
และการพิมพ์ผิดนั้นสามารถทำให้ใครบางคนหลุดออกไปได้เนื่องจากความcoutหมายที่แตกต่างกันมากcount
modulitos

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

แน่นอนว่าถ้าคุณอยากได้นักเล่นคุณสามารถเทมเพลตฟังก์ชั่นที่ใช้ฟังก์ชั่นที่ค้นพบและฟังก์ชั่นไม่พบได้เสมอ

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

และใช้มันเช่นนี้

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

ข้อเสียของเรื่องนี้กำลังเกิดขึ้นพร้อมกับชื่อที่ดี "find_and_execute" เป็นสิ่งที่น่าอึดอัดใจและฉันไม่สามารถหาอะไรที่ดีกว่าบนหัวของฉันได้ ...


3

โปรดใช้ความระมัดระวังในการเปรียบเทียบผลการค้นหากับจุดสิ้นสุดเช่นเดียวกับแผนที่ 'm' เนื่องจากคำตอบทั้งหมดทำไว้เหนือแผนที่ :: iterator i = m.find ("f");

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

คุณไม่ควรลองและดำเนินการใด ๆ เช่นการพิมพ์คีย์หรือค่าด้วย iterator i ถ้ามันเท่ากับ m.end () มิฉะนั้นจะทำให้เกิดการแบ่งส่วน


0

การเปรียบเทียบโค้ดของ std :: map :: find และ std :: map :: count, ฉันจะบอกว่าคนแรกอาจได้เปรียบด้านประสิทธิภาพ:

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

ฉันรู้ว่าคำถามนี้มีคำตอบที่ดีอยู่แล้ว แต่ฉันคิดว่าโซลูชันของฉันคุ้มค่าที่จะแบ่งปัน

มันทำงานได้ทั้งstd::mapและstd::vector<std::pair<T, U>>และสามารถใช้ได้จาก 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

หากคุณต้องการเปรียบเทียบคู่แผนที่คุณสามารถใช้วิธีนี้:

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

นี่เป็นเทคนิคที่มีประโยชน์


ในฐานะผู้เริ่มต้นด้วยการเขียนโปรแกรม C ++ ฉันสงสัยจริงๆว่าทำไมคำตอบนี้จึงถูกลดระดับลง ทำไมคำตอบนี้จึงไม่เป็นที่นิยม
gromit190

3
@ gromit190 เพราะใช้โครงสร้างข้อมูลอื่นทั้งหมดเพื่อดูว่ามีคีย์อยู่หรือไม่เมื่อ std :: map มีความสามารถนี้อยู่แล้ว สิ่งนี้จะต้องมีการซิงโครไนซ์ระหว่างโครงสร้างข้อมูลทั้งสองซึ่งเป็นการพึ่งพาที่ไม่มีใครต้องการจัดการ
Lambage

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

3
กรุณาอธิบายอย่างละเอียดเกี่ยวกับรหัสของคุณ ตัวอย่างที่ไม่มีคำอธิบายไม่น่าจะมีประโยชน์ในระยะยาว
iBug
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.