Fastest way to find if same struct exist in a vector - c++

Lets suppose that I have a persons struct:
struct Person {
char name[100];
char surname[100];
unsigned int age;
};
I would like to find out the fastest way to search and find if another struct with the same values (same name, same surname, same age) already exist in a vector.
Please keep in mind that I have million of those in a vector.
Thanks

Here is a possibility:
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <tuple>
struct Person {
std::string name;
std::string surname;
unsigned int age;
bool operator<(const Person &x) const
{
return std::tie(name, surname, age) < std::tie(x.name, x.surname, x.age);
}
};
int main()
{
std::vector<Person> v;
// ...
std::set<Person> s;
for (const auto &x : v)
{
auto i = s.insert(x);
if (!i.second)
{
// x is duplicated
}
}
}
To your comment, you can sort your vector by this way:
std::sort(v.begin(), v.end()); // Operator < is overloaded

Based on the comments in the question, specifically
No, I mean a set which describes that 10 was duplicate to 2, 12, 54, etc or 2 was duplicate to 10, 12, 54
it sounds like the data structure you actually want is std::multimap (or std::unordered_multimap if you have C++11 and don't care about order). Multimaps will take care of the bookkeeping you would have to do on your own with M M.'s solution (which is nice overall, except that you have to maintain an additional container with duplicate description). std::multimap does the extra bookkeeping for you.
#include <map> // or <unordered_map>
#include <string>
#include <tuple> // std::tie()
#include <utility> // std::make_pair()
struct Person {
std::string name;
std::string surname;
unsigned int age;
bool operator<(const Person &x) const
{
return std::tie(name, surname, age) < std::tie(x.name, x.surname, x.age);
}
};
extern bool tryReadNextPersonFromFile(Person &, size_t & record_id);
int main()
{
std::multimap<Person, size_t> persons;
Person p;
size_t rid;
while(tryReadNextPersonFromFile(p, rid)) {
persons.insert(std::make_pair(p, rid));
}
// ...
p = ...
size_t howMany = persons.count(p);
if(0 == howMany) { /* not found ... */ }
else {
auto eq_range = persons.equal_range(p);
for(auto it=eq_range.first; it != eq_range.second; ++it) {
size_t pRecordID = it->second;
// ...
}
}
}
I'm using a lot of C++11 syntax (like auto) for brevity, but this idea works just as well for C++03. Since you probably haven't heard of multimaps before (or at least are unfamiliar with the STL interface), be sure to check out eg, some documentation on what you can do with it and how.

Related

How to copy elements from std::list to an array of struct?

I need to copy the contents of a std::list into an array, wherein the array is struct of array. Below is the code implementation of it.
#include <iostream>
#include <string>
using namespace std;
typedef struct
{
int height;
int width;
int length;
}dimensions;
GetDimensions(list<std::string>, *int); // Function that copies the content of list to array passed as second parameter
int main()
{
dimensions cuboid[10];
int plane[10];
list<std::string> planeList = GetList();//Function that returns list of elements
list<std::string> dimensionList = GetList();
GetDimensions(planeList,&plane);//This is fine, as it is a simple array
GetDimensions(dimensionList,&cuboid.height);//Trouble in implementation of this usecase, for cuboid.height, cuboid.width and cuboid.height.
return 0;
}
GetDimensions(list<std::string>dimensionList, int* dimensionParams)
{
int i=0;
for(list<std::string>::iterator it = dimensionList.begin(); it != dimensionList.end(); ++it)
{
dimensionParams[i] = stoi(*it);
i++;
}
}
Here, I need GetDimensions() function to copy the list (passed as first parameter) to array (second parameter). The implemented function works well for simple array plane. But how to pass the array of struct as parameter to the function ?
I will be getting the std::list as cuboid.height, cuboid.width and cuboid.length. So the function has to copy the contents of list from cuboid[0].height to cuboid[i].height respectively. Is there any specific function to copy the content directly?
Use std::array 's instead. Then your problem can be reduced to passing two different types of arrays to a single function.
This can be solved
either by good old function overloads
or in c++17 function template with
if-constexpr.
Following is an example code with templated function with if-constexpr (See live online)
#include <iostream>
#include <string>
#include <list>
#include <array>
#include <type_traits> // std::is_same_v
struct dimensions // no need to typedef here
{
int height;
int width;
int length;
};
template<typename T>
void GetDimensions(const list<std::string>& dimensionList, T& dimensionParams)
^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //---> pass list by const-ref as the values are non-modifying
{
int i{0};
if constexpr (std::is_same_v<std::array<int, 10>, T>)
{
for(const std::string& str: dimensionList) dimensionParams[i++] = std::stoi(str);
}
else
{
for(const std::string& str: dimensionList) dimensionParams[i++].height = std::stoi(str);
}
}
int main()
{
std::array<dimensions, 10> cuboid; // use std::array instead of VLA
std::array<int, 10> plane;
std::list<std::string> planeList{"1", "2"}; // some list
std::list<std::string> dimensionList{"1", "2"};
GetDimensions(planeList, plane);
GetDimensions(dimensionList, cuboid);
return 0;
}
Also note that:
You have not specified the return type of GetDimensions function.
You probably want to return void there.
in C++ you do not need to use typedef alias for struct { ... }.
last but not least, do not practice with using namespace std;
You can do this with boost::transform_iterator.
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
struct dimensions {
int height;
int width;
int length;
};
template <typename OutputIt>
void GetDimensions(std::list<std::string> dimensionList, OutputIt dimensionParams)
{
// N.b. taking the address of a standard library function is undefined, so wrap in a lambda
auto stoi = [](std::string s){ return std::stoi(s); };
std::copy(boost::make_transform_iterator(dimensionList.begin(), stoi),
boost::make_transform_iterator(dimensionList.end(), stoi),
dimensionParams);
}
int main() {
dimensions cuboid[10];
int plane[10];
std::list<std::string> planeList = GetList();
std::list<std::string> heightList = GetList();
std::list<std::string> widthList = GetList();
std::list<std::string> lengthList = GetList();
GetDimensions(planeList, plane);
GetDimensions(heightList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::height)));
GetDimensions(widthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::width)));
GetDimensions(lengthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::length)));
return 0;
}

How do I add a string to a vector (and subsequently display it)?

I am 4 hours-new to C++ and have hit a brick wall with string vectors. When trying to add multiple strings to a string vector, I keep erroring out. I'd like to use push_back.
I would also like to display this string vector, but I'm not sure how (I know how to display non-vectors). Given that I have not been able to add a string to a vector of strings, I did not attempt to display the vector of strings yet.
profile.hpp
#include <iostream>
#include <vector>
class Profile
{
private:
std::string name;
std::string city;
std::string country;
int age;
std::vector<std::string> hobbies;
public:
std::vector<std::string> add_hobbies(std::string new_hobbies);
};
profile.cpp
#include <iostream>
#include "profile.hpp"
Profile::Profile(std::string new_name, int new_age, std::string new_city, std::string new_country)
: name(new_name), age(new_age), city(new_city), country(new_country)
{}
void Profile::add_hobbies(std::string new_hobbies)
{
hobbies.push_back(new_hobbies);
}
app.cpp
#include <iostream>
#include "profile.hpp"
int main()
{
Profile sam("Sam Drakkila", 30, "New York", "USA");
sam.add_hobbies("Play golf", "Read books", "Eat tennis balls"); // This doesn't seem to work.
}
g++ app.cpp profile.cpp. Prints a massive log of errors.
You have the following problems in your code:
You have declared add_hobbies returns std::vector<std::string>,
but in definition you have returned void. Presumably, you should have declared as a void function as it seems to be a setter function.
Secondly, you are passing a number of strings instead of a single string which
you defined here:
void Profile::add_hobbies(std::string new_hobbies) //>>> as per defenition, only one string can be passed!
// ^^^^^^^^^^^^^^^^^^^^^^^^
If you want to pass an arbitrary number of strings, you could usestd::initializer_list<std::string> instead.
Thirdly, you are missing the constructor declaration in the header file. Add
in the definition of class profile.hpp
Profile(std::string new_name, int new_age, std::string new_city, std::string new_country);
Last but not least, you need to include the <string> header in
order to use std::string(credits #πάντα ῥεῖ)
That means, (See live online)
#include <iostream>
#include <vector>
#include <string> // std::string
#include <initializer_list> // std::initializer_list
class Profile
{
private:
// ... other members
std::vector<std::string> hobbies;
public:
// ... other member functions
void add_hobbies(std::initializer_list<std::string> new_hobbies)
//^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
hobbies.reserve(hobbies.size() + new_hobbies.size()); // reserve memory to avoid, unwanted reallocations
for (const auto& hobby : new_hobbies) hobbies.emplace_back(hobby);
}
};
int main()
{
Profile sam{};
sam.add_hobbies({ "Play golf", "Read books", "Eat tennis balls" }); // now works
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
Alternatively using variadic templates and c++17 feature fold expression.
(See live online)
#include <iostream>
#include <vector>
#include <string> // std::string
#include <type_traits> // std::enable_if, std::is_same, std::common_type
using namespace std::literals;
class Profile
{
private:
// ... other members
std::vector<std::string> hobbies;
public:
// ... other member functions
template<typename... Args> // sfinae to restrict the Args type to be only std::string s
std::enable_if_t<std::is_same_v<std::common_type_t<Args...>, std::string>>
add_hobbies(Args&& ... args)
{
hobbies.reserve(hobbies.size() + sizeof...(Args));
(hobbies.emplace_back(std::forward<Args>(args)), ...);
}
};
int main()
{
Profile sam{};
sam.add_hobbies("Play golf"s, "Read books"s, "Eat tennis balls"s); // now works
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
You declare add_hobbies as returning a vector in your class declaration.
There are a few errors in your code:
Missing constructor declaration:
Profile(std::string new_name, int new_age, std::string new_city, std::string new_country);
Mismatch between return type of the declaration and definition of add_hobbies, should be void (since you're not returning anything).
void Profile::add_hobbies(std::string new_hobbies) { // ... }
You're also trying to pass 3 of them, while the function only has 1 parameter:
void add_hobbies(std::string const& h1, std::string const& h2, std::string const& h3);
// ...
void Profile::add_hobbies(std::string const& h1, std::string const& h2, std::string const& h3) {
hobbies.push_back(h1);
hobbies.push_back(h2);
hobbies.push_back(h3);
}

How to define a priority queue with four values in c++?

when we have two values like ID and Marks of a student and we want to put it in a queue we define the priority queue in the following way.
priority_queue<pair<int,int> > queue;
Let us say, that we have four values to put it into a queue. ie., I want all the three values as a single entity in the queue and I will define my own comaprator based on the three values. I'm new to STL and I could not find appropriate way of doing this.
You have std::tuple for such cases. And you write a comparator just like you would write it for a pair.
Joachim's idea of a struct is a good idea. I would probably take it one step further make the struct into its own class (let's call it DataNode). Then you can queue and dequeue the data node objects (each pointing to its own data) rather than passing the raw data into the queue.
#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <functional>
using namespace std;
struct Student
{
Student(string name, int id, int marks): name(name), id(id), marks(marks){}
string name;
int id;
int marks;
};
struct DereferenceCompareNode : public std::binary_function<Student, Student, bool>
{
bool operator()(const Student& lhs, const Student& rhs) const
{
if (lhs.id == rhs.id)
{
return lhs.marks >rhs.marks;
}
else
{
return lhs.id > rhs.id;
}
}
};
int main()
{
priority_queue<Student, vector<Student>, DereferenceCompareNode> a;
a.push(Student("yang", 1000, 98));
a.push(Student("yang", 1000, 75));
a.push(Student("zhang", 999, 98));
a.push(Student("zhang", 999, 100));
while( !a.empty() ){
Student top = a.top();
printf("%s, %d, %d\n", top.name.c_str(), top.id, top.marks);
a.pop();
}
cout<<"hello world"<<endl;
}
Here is the output:
wangyang#wangyang ~ $ ./a.out
zhang, 999, 98
zhang, 999, 100
yang, 1000, 75
yang, 1000, 98
I think you can use this code,
For usage of queue, please refer http://www.cplusplus.com/reference/queue/priority_queue/

Blacklisting offensive sentences

I've created a game meant for younger audiences and am trying to filter out profanity and offensive names
#include <iostream>
#include <vector>
bool isBanned( std::string text ) {
std::vector bannedSent = {
"Profanity1",
"Profanity2",
"Profanity3",
"Profanity4"
};
for(auto &i : bannedSent) {
if(text == i) { return true; }
}
return false;
}
I'm getting a compiler error talking about "template arguments", on the line with std::vector, what does this mean?
You need to supply template arguments to your vector. Since you are holding strings, you need to declare it like this:
std::vector< std::string > bannedSent = {
"Gosh",
"Golly",
"Jeepers",
"Troll"
};
The easiest solution is actually not to specify the type. The compiler already has a decent idea, and you already knew the keyword:
auto bannedSent = {
"Profanity1",
"Profanity2",
"Profanity3",
"Profanity4"
};
for(auto i : bannedSent) { ...
Side benefit: This avoid constructing 4 std::string objects in each call.
Note that you used auto& i earlier. That's a mistake, you don't intend to change bannedSent.
If should be std::vector<std::string>:
bool isBanned( std::string text ) {
std::vector<std::string> bannedSent = {
...
}
}
Since you include the C++11 tag, you can also use any_of():
#include <vector>
#include <string>
#include <algorithm>
bool isBanned(const std::string & text)
{
const std::vector<std::string> bannedSent = {
"Profanity1",
"Profanity2",
"Profanity3",
"Profanity4",
};
return std::any_of(bannedSent.begin(), bannedSent.end(), [text](std::string &s){return s == text; });
}

find strings in vector with equal_to function object

I have following code:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <functional>
using namespace std;
typedef istream_iterator<string> is_it;
typedef vector<string>::iterator v_str_it;
int main()
{
int i = 4;
ifstream ifstr("1.txt");
is_it ifstrm(ifstr);
is_it eof;
vector<string> v_str(ifstrm, eof);
v_str_it vsit = v_str.begin();
while( (vsit = find_if(vsit, v_str.end(),
bind2nd(equal_to<string>(), i ))) != v_str.end())
{
cout << *vsit << endl;
++vsit;
}
return 0;
}
As far as I understand in find_if(vsit, v_str.end(), bind2nd(equal_to<string>(), i ) i should use const char like "sometext" instead of int i. But how can i find words with length equal to 4 e.g. ? I'm confused and need some advice.
find_if will only return the first item in the sequence that satisfies the predicate.
For this you really want a lambda and if you are using C++11. This will look something like:
[](std::string const& x) { return x.size() == i; }
(Not sure of the exact syntax).
To create a "functor" which is the simplest here you might do:
struct CompareStringLength
{
int len_;
explicit CompareStringLength( int len ) : len_(len)
{
}
bool operator()(std::string const& str ) const
{
return str.size() == len_;
}
};
Within your vector you would now use std::find_if( v.begin(), v.end(), CompareStringLength(i) );
to get the first element. To find all of them there is no std::copy_if to copy them into another vector so you'd actually have to create a different predicate that returns the opposite and use remove_copy_if which does exist or write your own copy_if algorithm.