Нажатие уникальных данных в вектор
У меня есть следующие данные:
FolioA Name1 100
FolioA Name2 110
FolioA Name3 100
FolioB Name1 100
FolioB Name3 106
FolioC Name1 108
FolioC Name2 102
FolioC Name3 110
Я хочу только вставлять уникальные имена (например, Name1, Name2 и Name3, каждый раз) в
std::vector<std::string> name;
когда я повторяю данные.
Итак, у меня есть следующий код, где я сохранил данные на карте с именем 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;
}
Но, в настоящее время, нажимая данные на имя, у меня есть 3 экземпляра Name1, 2 из Name2 и 3 Name3, которые ожидаются от моего кода. Как исправить это, чтобы иметь только уникальные имена.
Ответы
Ответ 1
Поскольку вы хотите сохранить первый экземпляр для данного имени, вам нужно будет выполнить поиск имени в какой-то момент. Простой алгоритм, включающий только ваш вектор, состоял бы в том, чтобы проверить, существует ли эта запись, используя 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);
}
Но здесь вы выполняете поиск каждый раз, когда хотите вставить элемент, и это (само по себе) до сложности O(N)
, предоставляя O(N*N)
для всего алгоритма. Таким образом, вы можете оптимизировать, используя промежуточный контейнер с быстрым поиском, например std::set
, как это было предложено @Chad, и который имеет сложность O(logN)
для поиска, предоставляя O(N*logN)
в целом или хэш-контейнер, такой как С++ 11 std:: unordered_set, который близок к постоянному поиску по времени, что дает общую сложность 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);
а затем, следуя примеру @,
std::vector<std::string> name(names_set.begin(), name_set.end());
Если у вас нет С++ 11, альтернативы хэш-карты - boost::hash_map
и tr1::hash_map
.
Ответ 2
В случае, если вам все равно, какой экземпляр вы хотите ввести в свою структуру данных, std:: set будет сервер вашей цели
Ответ 3
Может быть, вы должны использовать другую карту istead для вектора, чтобы иметь уникальные имена.
std:: map < std::string, double > name;
Ответ 4
Вы попросили образец кода, так вот как бы я это сделал:
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());
Ответ 5
список имеет возможность .sort(), а затем .unique(), которая предоставит вам.
вы можете перебрать его с помощью итератора и инициализировать его с помощью initializer_list.
эти данные больше похожи на структуру для меня:
#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/
Ответ 6
Если вы хотите использовать std::vector и не заботитесь о сложности выполнения (это O (n ^ 2), это более идиоматический способ:
void add_once(std::vector<std::string>& vec, const std::string& element) {
std::remove(vec.begin(), vec.end(), element);
vec.push_back(element);
}