Blacklisting offensive sentences - c++

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

Related

C++: How to make function return only strings which are a part of a list?

I want my function to return a string, but only strings which are a member of a specific list/set of strings. How can I go about doing this?
You do not want to return a string, you want to return a string that has an additional restriction (being part of some predefined set).
For that you'd need a new type:
class BusinessStringWrapper {
public:
BusinessStringWrapper(std::string arg): value{arg} {
if (/* arg is not ok */) {
throw;
}
}
// you can replace that with factory method
// can also return std::optional instead of throwing if the condition is not met
// that depends on your application
std::string value() const { return value; }
private:
const std::string value;
};
And in your application you'd operate on this type, accessing value if needed.
Hoe about using a std::set<std::string>?
#include <iostream>
#include <set>
#include <string>
std::string helper(const std::string & str,
const std::set<std::string> & lst)
{
return lst.find(str) == lst.end() ? "" : str;
}
int main()
{
std::set<std::string> lst = {"alpha", "beta", "gamma"};
std::cout << "return " << helper("alpha", lst) << "\n";
std::cout << "return " << helper("zeta", lst) << "\n";
return 0;
}
Output
return alpha
return
Of course, it really depends on what your definition of does not return is.
If it means an empty string, then use the above solution. Keep your life simple.
If it means an error and the program should terminate, you may #include <cassert> and just
assert(lst.find(str) != lst.end());
If it means an exception to handle, you may try throw and catch.
If it means returning a std::string if str is in a predefined list, but a void if it's not, then you may need some tricks as described in <type_traits>.
You can do this std::map<CardType, std::string> in the example below, or use std::map<int, std::string> to associate a string with any integer. For example mp[123]="abcd"
#include <iostream>
#include <string>
#include <map>
enum CardType {
SPADE,
HEART,
CLUBS,
DIAMD
};
std::map<CardType, std::string> mp{
{CardType::SPADE, "Spade"},
{CardType::HEART, "Heart"},
{CardType::CLUBS, "Clubs"},
{CardType::DIAMD, "Diamond"}
};
int main()
{
std::cout << mp[CardType::SPADE] << std::endl;
return 0;
}

Extracting a class member from a vector of class instances (as a new vector)

I have a class/structure as follows:
struct MyStruct {
std::string member_one;
std::string member_two;
};
I create a vector of MyStruct, std::vector<MyStruct> of some length N with the members set to custom values:
std::vector<MyStruct> my_struct_vect(10);
// initialize class instances
Now I want to extract the first member memberOne into a new vector. I can do so as follows:
std::vector<std::string> member_one_vect(my_struct_vect.size());
for (size_t i = 0; i < my_struct_vect.size(); ++i) {
member_one_vect[i] = my_struct_vect[i].member_one;
}
My question is, is there a quicker/more elegant/cleaner way of doing this without having to write the custom loop every time? In Python for example, I could do this quite easily with comprehensions. I'm not expecting something similar in C++, but am wondering if there is some way to simplify this nonetheless.
Update
Thanks to the great responses on using std::transform and boost::adaptors::transformed. These are very useful, but in order to be compact it's worth noting they rely on lambda functions which were introduced in C++11 (it's possible to use them without, but this requires defining a separate helper function).
So for bonus points, are there any ways to do this in a compact way in C++03?
You could use std::transform:
#include <algorithm> // Necessary for std::transform()
// ...
std::vector<std::string> member_one_vect(my_struct_vect.size());
std::transform(
my_struct_vect.begin(), my_struct_vect.end(), member_one_vect.begin(),
[] (MyStruct const& ms)
{
return ms.member_one;
});
Here is how the full code would look like:
#include <vector>
#include <string>
#include <algorithm>
struct MyStruct {
std::string member_one;
std::string member_two;
};
int main()
{
std::vector<MyStruct> my_struct_vect(10);
// Initialize my_struct_vect...
std::vector<std::string> member_one_vect(my_struct_vect.size());
std::transform(
my_struct_vect.begin(), my_struct_vect.end(), member_one_vect.begin(),
[] (MyStruct const& ms)
{
return ms.member_one;
});
// Do something with member_one_vect...
}
Here is a live example.
You can use algorithms.
std::vector<std::string> member_one_vect(my_struct_vect.size());
std::transform(my_struct_vect.begin(), my_struct_vect.end(),
member_one_vect.begin(),
[](const MyStruct& m) { return m.member_one; });
Also, you can use boost::adaptors::transformed.
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
std::vector<std::string> member_one_vect(my_struct_vect.size());
std::function<std::string(const MyStruct&)> transform =
[] (const MyStruct& m) { return m.member_one; };
boost::copy(my_struct_vect | boost::adaptors::transformed(transform),
member_one_vect.begin());

Fastest way to find if same struct exist in a vector

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.

Multiple similar functions coding style

I have a large series of functions that all look very similar: they take the same arguement type and return strings.
std::string f1(T arg);
std::string f2(T arg);
std::string f3(T arg);
std::string f4(T arg);
.
.
.
In a loop, they are used according to one of the variables inside the struct T. Currently to do this, I just have a large switch/case block in my code.
Is there any better coding style for doing this? The large block of code looks very weird.
I wish c++ could be like python and do eval("f" + str(i) + "(arg))"
The block is something like this:
std::string out = "";
switch (arg.tag){
case 1:
out += f1(arg);
break;
case 2:
out += f2(arg);
break;
.
.
.
}
for about 2 dozen cases
With C++11 you can do this fairly easily with std::function and a map:
#include <map>
#include <functional>
#include <string>
#include <iostream>
std::string f1(int) { return "f1"; }
std::string f2(int) { return "f2"; }
std::map<int, std::function<std::string(int)> > funcs = {
{1,f1},
{2,f2}
};
int main() {
std::cout << funcs[1](100) << "\n";
}
Without C++11 you'll want to either use Boost instead of std::function or roll your own type instead. You could use plain old function pointers but that would rule out some handy things (like std::bind/boost::bind, functor objects, lambda functions. You could also define a type hierarchy with an interface that your functions implement for example the following works in C++03 except for the way the map is initialised:
#include <map>
#include <functional>
#include <string>
#include <iostream>
std::string f1(int) { return "f1"; }
std::string f2(int) { return "f2"; }
std::map<int, std::string(*)(int)> funcs = {
std::make_pair(1,f1),
std::make_pair(2,f2)
};
int main() {
std::cout << funcs[1](100) << "\n";
}
or this which lets you write any kind of functor object you like:
#include <map>
#include <string>
#include <iostream>
struct thing {
virtual std::string operator()(int) const = 0;
};
struct f1 : thing {
std::string operator()(int) const { return "f1"; }
};
struct f2 : thing {
std::string operator()(int) const { return "f2"; }
};
// Note the leak - these never get deleted:
std::map<int, thing*> funcs = {
std::make_pair(1,new f1),
std::make_pair(2,new f2)
};
int main() {
std::cout << (*funcs[1])(100) << "\n";
}
One way to emulate the Eval() is to have a map. The key of the map would be the names of the functions, and the values would be the pointers to the corresponding functions.
In this case you will be able to call the functions needed with the map's operator[] by their name. This will somehow emulate the eval("f" + str(i) + "(arg))" behavior, though it may still not be the best solution for you.

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.