quinta-feira, 3 de novembro de 2011

Map de string em C++

Uma das grandes vantagens de programar em C++ ao invés de C é a utilização da STL (Standard Template Library). Ela disponibiliza Containers, Iterators, Algoritmos e Funções Objetos. Nesse post irei abordar uma característica de busca no container MAP e MULTIMAP.

Container são objetos que armazenam outros objetos e o método de acessar esses objetos. Por exemplo, posso contruir um vetor de inteiros (vector<int> v). Qual a vantagem de utilizar um vector ao invés de criar um elemento do tipo int v[10]? Se você reparar, verá que na declaração do vector eu não especifiquei o tamanho. Isso mesmo, ele é auto dimensionável. Ele também tem algumas funções que facilitam a manipulação de seus elementos.

Outra coisa muito interessante que C++ tem é a classe string. A classe string é um container de char's, que tem algumas funções que facilitam a manipulação dos elementos. Também é auto redimensionável.

Os containers MAP e MULTIMAP são containers associativos com 2 elementos, uma chave e um dado. A diferença é que no map, a chave é única, e no multimap, a chave pode se repetir. A construção básica de um map seria map<int, string> m, onde o primeiro elemento é a chave.

É possível acessar o elemento através de sua chave, por exemplo, tenho um map com os seguintes pares: 1, primeiro; 2, segundo; 3, terceiro; 4, quarto. A função m.find(3) irá retornar um iterator (que deve ser declarado como map<int, string> it, por exemplo) que aponta para o elemento de chave 3. Para acessar o dado, eu faria string elemento = (*it).second.

Mas e se a chave for uma string (map<string, string> mstring), a busca será case sensitive. Para realizar busca case insensitive é preciso implementar o método de busca, e ele deve ser especificado na declaração da variável.

No caso, eu criei um utils.h com o seguinte código:

#ifndef UTILS_H
#define UTILS_H
#include <string>
#include <algorithm>
using namespace std;
struct ltstr
{
    bool operator()(string s1, string s2) const
    {
        string s1Tmp, s2Tmp;
        s1Tmp = s1;
        s2Tmp = s2;
        transform(s1Tmp.begin(), s1Tmp.end(), s1Tmp.begin(), ::toupper);
        transform(s2Tmp.begin(), s2Tmp.end(), s2Tmp.begin(), ::toupper);
        return s1Tmp.compare(s2Tmp) < 0;
    }
};
#endif // UTILS_H

Para declarar a variável map basta fazer como abaixo:


#include <map>
#include <string>
#include "utils.h"
#include <iostream>
using namespace std;
int main()
{
    map<string, string, ltstr> mstring;
    map<string, string, ltstr>::iterator it;
    mstring.insert(pair<string, string>("a", "primeiro"));
    mstring.insert(pair<string, string>("b", "segundo"));
    mstring.insert(pair<string, string>("c", "terceiro"));
    mstring.insert(pair<string, string>("D", "quarto"));
    mstring.insert(pair<string, string>("e", "quinto"));
    it = mstring.find("d");
    if (it != mstring.end())
    {
        cout << "elemento procurado: " << (*it).second.c_str() << endl;
    }
    return 0;
}



Essa solução funciona também para o elemento multimap.