MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

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