web-dev-qa-db-fra.com

Transférer des données uniques en vecteur

J'ai les données suivantes:

FolioA Name1 100
FolioA Name2 110
FolioA Name3 100
FolioB Name1 100
FolioB Name3 106
FolioC Name1 108
FolioC Name2 102
FolioC Name3 110

Je souhaite insérer uniquement des noms uniques (c.-à-d. Nom1, Nom2 et Nom3, chacun une fois) dans

std::vector<std::string> name;

comme je le répète à travers les données.

Donc, j'ai le code suivant où j'ai stocké les données dans une carte appelée test:

std::map<std::string, std::map<std::string, double> >test;
std::map<std::string, std::map<std::string, double > >::iterator it1 = test.begin(), end1 = test.end();
    while (it1 !=end1) {
        std::map<std::string, double>::iterator it2 = it1->second.begin(), end2=it1->second.end();
        **name.Push_back(it2->first);**
        ++it2;
    }
    ++it1;
}

Mais, actuellement, en poussant les données dans le nom comme je le fais, j'ai 3 instances de Name1, 2 de Name2 et 3 de Name3, ce qui est attendu de mon code. Comment puis-je le corriger pour n'avoir que des noms uniques.

22
user1155299

Étant donné que vous souhaitez conserver la première instance pour un nom donné, vous devrez effectuer une recherche de nom à un moment donné. Un algorithme simple impliquant uniquement votre vecteur serait de pouvoir vérifier si l'entrée existe déjà en utilisant std :: find

std::vector<std::string> name;

....
if (std::find(name.begin(), name.end(), someName) == name.end()) {
  // someName not in name, add it
  name.Push_back(someName);
}

Mais ici, vous effectuez une recherche à chaque fois que vous souhaitez insérer un élément, et cela (par lui-même) est jusqu'à O(N) complexité, donnant O(N*N) pour tout l'algorithme. Vous pouvez donc optimiser en utilisant un conteneur intermédiaire avec une recherche rapide, tel qu'un std::set Comme suggéré par @Chad et qui a O(logN) complexité pour la recherche, donnant O(N*logN) global, ou un conteneur de hachage tel que C++ 11's std :: unordered_set , qui a une recherche à temps constant, ce qui donne une complexité globale de ~ O (N).

std::unordered_set name_set;
....

// still need to search, since you want to keep 
// the first instance of each name, and not the last.
// But unordered_set performs the look-up at insertion,
// only inserting if someName not already in the set
name_set.insert(someName);

puis, suivant l'exemple de @ Chad,

std::vector<std::string> name(names_set.begin(), name_set.end());

Si vous n'avez pas C++ 11, les alternatives de mappage de hachage sont boost::hash_map Et tr1::hash_map.

33
juanchopanza

Vous avez demandé un exemple de code, alors voici comment je l'aurais fait:

std::set<std::string> unique_names;

// ...
while (it1 !=end1)
{
    // ...
    // **name.Push_back(it2->first);**
    unique_names.insert(it2->first);
}

std::vector<std::string> name(unique_names.begin(), unique_names.end());
3
Chad

list a la possibilité de .sort () puis .unique (), qui vous fournira.

vous pouvez le parcourir avec un itérateur et l'initialiser avec initializer_list.

ces données me ressemblent plus à une structure:

#include <iterator>
#include <list>
#include <string>
#include <fstream>

typedef struct NODE_S {
    string name1, name2;
    int n;
} NODE_S NODE;

bool compare_NODE (NODE first, NODE second)
{
    unsigned int i=0;
    if (first.name1 < second.name1) {
        return true;
    } else if (first.name2 < second.name2) {
        return true;
    } else if (first.n < second.n) {
        return true;
    } else { return false;}
}


bool readfile(list<NODE>& ln, string filepath) {
    std::ifstream filein;
    NODE n;
    filein.open(filepath.c_str(), std::iofstream::in);
    if (!filein.good()) {
        filein.close();
        std::cerr << "ERROR: unable to open file \"" << filepath << "\" or file is zero-length." << std::endl;
        return false;
    }
    do {
        filein >> n.name1 >> n.name2 >> n.name3 >> std::skipws;
        ln.Push_back(n);
        ln.sort(compare_NODE);
        ln.unique();
        //add node to list

    } while (!filein.good()); //can use .eof here, but if bad disk blocks...
    filein.close();
    return true;
}


int main(int argc, char * argv[], char * envp[]) {
    string filepath="somefile.txt";
    if (!readfile(filepath)) {
        return 1;
    }
    list<NODE>::iterator lni;
    for (lni = ln.begin(); lni != ln.end(); lni++) {
        std::cout<<lni->name1<<' '<<lni->name2<<' '<<lni->n<<std::endl;
    }
    return 0;
}

http://www.cplusplus.com/reference/stl/list/sort/

http://www.cplusplus.com/reference/stl/list/unique/

2
Jim Michaels

Dans le cas où vous ne vous souciez pas de l'instance que vous souhaitez entrer dans votre structure de données, std :: set servirait votre objectif

2
Abhijit

Vous devriez peut-être utiliser une autre carte au lieu d'un vecteur pour avoir des noms uniques.

std :: map <std :: string, double> nom;

2
phantasmagoria