Dicționarul std::map
std::map (sau hartă) este un container în care fiecare valoare este asociată cu o anumită cheie. Prin această cheie putem accesa elementul. Cheile trebuie să fie unice. Un exemplu clasic de astfel de container este un dicționar, în care fiecărui cuvânt îi corespunde o traducere sau o explicație. De aceea, aceste structuri se mai numesc și dicționare.
Biblioteca standard C++ oferă două tipuri de dicționare: std::map<Key, Value> și std::unordered_map<Key, Value>. Ambele sunt șabloane care folosesc doi parametri de tip:
- Key — tipul cheilor
- Value — tipul valorilor
Tipul std::map este definit în fișierul antet <map>. Definirea unui dicționar gol:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products;
}
Aici am definit dicționarul products, care va reține (convențional) prețurile unor produse. Cheile sunt de tip std::string, iar valorile sunt numere unsigned (cheia reprezintă numele produsului, iar valoarea – prețul).
Accesarea elementelor
Pentru a accesa elementele din dicționar – fie pentru a le citi, fie pentru a le modifica – se folosește operatorul de indexare [], exact ca la vectori sau tablouri. Doar că în loc de indici întregi, se folosesc chei de orice tip:
map[cheie] = valoare;
Exemplu:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products;
// stabilim valorile
products["bread"] = 30;
products["milk"] = 80;
products["apple"] = 60;
// obținem valorile
std::cout << "bread\t" << products["bread"] << std::endl;
std::cout << "milk\t" << products["milk"] << std::endl;
std::cout << "apple\t" << products["apple"] << std::endl;
}
Aici, dicționarul products are chei de tip std::string și valori de tip unsigned. Pentru a seta o valoare, introducem cheia în paranteze pătrate și atribuim o valoare:
products["bread"] = 30;
Presupunem că cheia este numele produsului, iar valoarea este prețul. Dacă cheia nu există, se creează automat un nou element. Dacă cheia există deja, valoarea va fi suprascrisă.
Pentru a accesa valoarea aferentă unei chei:
unsigned breadPrice = products["bread"];
Valorile în consolă:
bread 30
milk 80
apple 60
Parcurgerea elementelor
Putem parcurge elementele din dicționar cu un ciclu for-each:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products;
// stabilim valorile
products["bread"] = 30;
products["milk"] = 80;
products["apple"] = 60;
for (const auto& [product, price] : products)
std::cout << product << "\t" << price << std::endl;
}
Fiecare element din dicționar este de tip std::pair<const Key, Value>, care conține cheia și valoarea. În cazul nostru, tipul este std::pair<const std::string, unsigned int>. Putem accesa aceste părți astfel:
for (const auto& element : products)
std::cout << element.first << "\t" << element.second << std::endl;
Începând cu C++17, putem folosi și o sintaxă modernă de „destructurare”, care extrage direct cheia și valoarea:
for (const auto& [product, price] : products)
std::cout << product << "\t" << price << std::endl;
Aici, product va conține cheia, iar price valoarea corespunzătoare.
Ieșirea în consolă va fi:
apple 60
bread 30
milk 80
Observație: elementele din std::map sunt ordonate în funcție de chei. Deoarece cheile sunt șiruri de caractere, ele vor fi sortate alfabetic.
Inițializarea elementelor
Faptul că elementele dintr-un dicționar sunt de tip std::pair permite inițializarea dicționarului cu obiecte de tip std::pair:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products
{
std::pair<std::string, unsigned>{"bread", 30}, std::pair{"milk", 80}, std::pair{"apple", 60}
};
}
Putem chiar să scurtăm definiția:
Ștergerea elementelor
După cum am văzut mai devreme, pentru a adăuga un element într-un dicționar este suficient să atribuim o valoare unei chei. Pentru a șterge un element, folosim funcția erase(), căreia îi transmitem cheia:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products
{
{"bread", 30}, {"milk", 80}, {"apple", 60}
};
products.erase("milk"); // ștergem elementul cu cheia "milk"
for (const auto& [product, price] : products)
std::cout << product << "\t" << price << std::endl;
// ieșire în consolă:
// apple 60
// bread 30
}
Dimensiunea dicționarului
Pentru a obține numărul de elemente din dicționar se folosește funcția size(). Clasa map oferă și funcția empty(), care returnează true dacă dicționarul este gol:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products
{
{"bread", 30}, {"milk", 80}, {"apple", 60}
};
std::cout << "Products count: " << products.size() << std::endl;
std::cout << "Products is empty: " << std::boolalpha << products.empty() << std::endl;
}
Verificarea existenței unui element
Pentru a verifica dacă un element cu o anumită cheie există în dicționar, putem folosi funcțiile count() (returnează 1 dacă elementul există și 0 altfel) și contains() (returnează true sau false). Ambele funcții primesc cheia ca parametru:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, unsigned> products
{
std::pair<std::string, unsigned>{"bread", 30}, std::pair{"milk", 80}, std::pair{"apple", 60}
};
std::cout << "Apple\t" << products.count("apple")<< std::endl;
std::cout << "Orange\t" << products.count("orange")<< std::endl;
std::cout << "Apple\t" << std::boolalpha << products.contains("apple")<< std::endl;
std::cout << "Orange\t" << std::boolalpha << products.contains("orange")<< std::endl;
}
Dicționare neordonate
Tipul std::map definește un dicționar care ordonează automat toate elementele – în mod implicit, în ordine crescătoare după cheie. Dacă nu avem nevoie de ordonare, putem folosi std::unordered_map, care oferă același set de funcții, dar nu ordonează elementele. Este definit în <unordered_map>:
#include <iostream>
#include <unordered_map>
int main()
{
std::unordered_map<std::string, unsigned> products
{
std::pair<std::string, unsigned>{"bread", 30}, std::pair{"milk", 80}, std::pair{"apple", 60}
};
for (const auto& [product, price] : products)
std::cout << product << "\t" << price << std::endl;
}
Ieșire posibilă în consolă:
apple 60
milk 80
bread 30
Iteratori
Este important de menționat că iteratorii pentru std::map sunt constanți, ceea ce înseamnă că nu putem modifica valorile elementelor direct în timpul parcurgerii:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, std::string> phoneBook
{
{"+11111111111", "Tom"}, {"+12222222222", "Bob"}, {"+13333333333", "Sam"}
};
for(auto iter{phoneBook.begin()}; iter != phoneBook.end(); iter++)
{
std::cout << iter->first << "\t" << iter->second << std::endl;
}
// pentru obținerea iteratorilor constanți se pot folosi și cbegin/cend
for(auto iter{phoneBook.cbegin()}; iter != phoneBook.cend(); iter++)
{
std::cout << iter->first << "\t" << iter->second << std::endl;
}
}