Is there a simple way of refactoring this code? - c++

I have a function that have very similar repeating code. I like to refactor it, but don't want any complex mapping code.
The code basically filter out columns in a table. I made this example simple by having the comparison statement having a simple type, but the real comparison can be more complex.
I am hoping there may be some template or lambda technique that can do this.
vector<MyRecord*>& MyDb::Find(bool* field1, std::string * field2, int* field3)
{
std::vector<MyRecord*>::iterator iter;
filterList_.clear();
std::copy(list_.begin(), list_.end(), back_inserter(filterList_));
if (field1)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field1 != *field1)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
if (field2)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field2 != *field2)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
if (field3)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field3 != *field3)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
return filterList_;
}
Update: Just in case someone is curious, this is my final code. Thanks again everyone. A lot easy to understand and maintain.
vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3)
{
auto compare = [&](MyRecord* rec) {
bool add = true;
if (field1 && rec->field1 != *field1) {
add = false;
}
if (field2 && rec->field2 != *field2) {
add = false;
}
if (field3 && rec->field3 != *field3) {
add = false;
}
return add;
};
filterList_.clear();
std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_), compare);
return filterList_;
}

you can use std::copy_if (as you already/would do a copy anyway)
vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3){
filterList_.clear();
std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_),[&](MyRecord* rec){
// whatever condition you want.
return field3 && rec->field3 != *field3;
});
return filterList_;
}

Is there a simple way of refactoring this code?
As far as I understood your algorithm/ intention, using std::erase_if (c++20) you can replace the entire while loops as follows (Demo code):
#include <vector> // std::erase_if
std::vector<MyRecord*> // return by copy as filterList_ is local to function scope
Find(bool* field1 = nullptr, std::string* field2 = nullptr, int* field3 = nullptr)
{
std::vector<MyRecord*> filterList_{ list_ }; // copy of original
const auto erased = std::erase_if(filterList_, [=](MyRecord* record) {
return record
&& ((field1 && record->field1 != *field1)
|| (field2 && record->field2 != *field2)
|| (field3 && record->field3 != *field3));
}
);
return filterList_;
}
If no support for C++20, alternatively you can use erase–remove idiom, which is in effect happening under the hood of std::erase_if.

Related

Is there a better way to detect multiple occurrences of an item in my vector?

I'm a beginner to the STL and I used it to make a simple hangman project. Full code here: https://github.com/SamtheSaint/Hangman.
I needed to detect multiple occurrences of letters in a vector but I could not and ended up working around it to finish the program. Is there a simpler way to do this?
iter = find(gameWord.begin(), gameWord.end(), playGuess);
if (iter == gameWord.end()) {
guesses--;
}
while (iter != gameWord.end()) {
iter = find(gameWord.begin(), gameWord.end(), playGuess);
if (iter != gameWord.end()) {
int index = distance(gameWord.begin(), iter);
hiddenWord[index] = playGuess;
*iter = '0'; // so program can find the next one
}
}
I end up destroying gameWord vector so I have to make a copy(which I call checkWord) at the beginning of the loop it's in so I can compare it later to hiddenWord.
You do not need std::map.
You just need two std::string (one which is expression to guess, the other one is the pattern shown to the player) which will be kept in synchronization. This mean you should enclose them in class.
Do not make thing more complicated then it is necessary.
This is quite simple:
class Hangman {
public:
constexpr SecretChar = '_';
Hangman(const std::string& secret)
: mSecret(secret)
{
provideMask();
}
bool guess(char ch) {
auto index = mSecret.find(ch);
if (index == std::string::npos) return false;
if (already_guessed(index)) return false;
updateMaskWith(ch);
return true;
}
std::string mask() const {
return mMask;
}
private:
void provideMask() {
mask = mSecret;
std::replace_if(mMask.begin(), mMask.end(), std::isalpha, SecretChar);
}
bool already_guessed(int index) {
return mMask[index] != SecretChar;
}
void updateMaskWith(char ch) {
auto index = mSecret.find(ch);
while (index == std::string::npos) {
mMask[index] = ch;
index = mSecret.find(ch, index + 1);
}
}
private:
std::string mSecret;
std::string mMask;
};
Now write seperate code which will use this and keep score and you are almost done.

Modification to STL List Contents in C++

In the below Code-snippet, I am trying to manipulate the contents of each of the lists present in the MAP mp but by returning a pointer to list corresponding map's key whose list needs modification. I am aware that a direct modification of the list contents is possible instead of calling getlist and then modifiying it, but I am new to STL and C++ and trying to learn STL by playing around a bit with Iterators and Lists.
When the below code is executed, a Segmentation fault is thrown at the line "(*lit) = 10". Can anyone help me understand what's going wrong here?
static void getlist(int num,map<int,list<int>> mp, list<int>** l_ptr )
{
map<int,list<int>>::iterator it = mp.begin();
while( it != mp.end())
{
if(it->first == num )
{
*l_ptr = &(it->second);
return;
}
it++;
}
}
int main()
{
map<int,list<int>> mp;
mp[1] = {2,2,2};
mp[2] = {3,3,3};
mp[3] = {4,4,4};
map<int,list<int>>::iterator it = mp.begin();
list<int>::iterator lit;
list<int>* r_l = new list<int>;
//getlist(it->first,mp,r_l);
while( it != mp.end())
{
getlist(it->first,mp,&r_l);
lit = r_l->begin();
while(lit != r_l->end())
{
(*lit) = 10;
lit++;
}
it++;
}
it = mp.begin();
while( it != mp.end())
{
lit = (it->second).begin();
while(lit != (it->second).end())
{
cout<<(*lit);
lit++;
}
it++;
}
return 0;
}

Parsing object inside array in rapidjson

I'm having problems implementing a recursive function that goes over the tree I get from the parsing of a json input.
json input. e.g.:
{
"attr" : { "a": 1, "ovec": [ { "b": 2, "c": 3 }, { "d": 4} ] }
}
This is what we call a 'compound value of an attribute', and the value is simply a JSON doc. Its content is completely arbitrary (as long as its valid JSON).
The problem is that with a Vector I have to loop using the type Value::ConstValueIterator (unlike for Object, where I use Value::ConstMemberIterator).
My recursive function has Value::ConstMemberIterator as parameter and all is OK until I encounter a Vector/Object inside a Vector - for the recursive call I'd need an iterator of the type Value::ConstMemberIterator.
Relevant parts of the "traversing" function:
int parseContextAttributeCompoundValue
(
const Value::ConstMemberIterator& node
)
{
std::string type = jsonParseTypeNames[node->value.GetType()];
if (type == "Array")
{
for (Value::ConstValueIterator iter = node->value.Begin(); iter != node->value.End(); ++iter)
{
std::string nodeType = jsonParseTypeNames[iter->value.GetType()];
if (nodeType == "String")
{
val = iter->GetString();
}
// else if ...
if ((nodeType == "Object") || (nodeType == "Array"))
{
// Here's my problem - need to convert 'iter' to Value::ConstMemberIterator
// in order to recursively call parseContextAttributeCompoundValue for this object/array
parseContextAttributeCompoundValue(iter); // COMPILATION ERROR
}
}
}
else if (type == "Object")
{
for (Value::ConstMemberIterator iter = node->value.MemberBegin(); iter != node->value.MemberEnd(); ++iter)
{
std::string nodeType = jsonParseTypeNames[iter->value.GetType()];
if (nodeType == "String")
{
val = iter->value.GetString();
}
else if (nodeType == "Number")
{
if ((nodeType == "Object") || (nodeType == "Array"))
{
// Here I'm just fine as iter is of the desired type already
parseContextAttributeCompoundValue(iter);
}
}
}
}
I've tried a few things like calling iter->value.MemberBegin() to "convert" to the desired type, but so far without any success
More than thankful for some help here ...
You can simply call a function with a Value type, instead of passing iterator:
void parseContextAttributeCompoundValue(const Value& v) {
if (v.IsObject()) {
// ...
}
else if (v.IsArray() {
// ...
}
}
And then from the calling site:
for (Value::ConstValueIterator iter = ...) {
parseContextAttributeCompoundValue(*iter);
}
for (Value::ConstMemberIterator iter = ...) {
parseContextAttributeCompoundValue(iter->value);
}

vector iterator incrementable when erasing element of vector in 2 for loops

I am currently programming a little game for the console with an 2D map. 2 Elements of my game are: destroying fields and an enemy, which spreads in a random direction (its getting bigger). These two "entities" are saved in a structure which contains two vectors (X and Y). I am now trying to erase an element of "_Enemy"(<-private instance of the structure in a class, same as "_DestroyedFields") if you destroy the field where the enemy is.
I tried a lot of different variations to do so and whats giving me the error least is this method (I already searched the internet for a while now an couldn't find a answer to my question):
for (std::vector<int>::iterator itEX = _Enemys.X.begin(), itEY = _Enemys.Y.begin();
itEX != _Enemys.X.end() && itEY != _Enemys.Y.end();
++itEX, ++itEY) {
for (std::vector<int>::iterator itX = _DestroyedFields.X.begin(),
itY = _DestroyedFields.Y.begin();
itX != _DestroyedFields.X.end() && itY != _DestroyedFields.Y.end();
++itX, ++itY) {
if (*itY == *itEY && *itX == *itEX){
itEY = _Enemys.Y.erase(itEY);
itEX = _Enemys.X.erase(itEX);
}
}
}
PS: sorry if my english isn't the best, im german ^^
PSS: if you wanna watch over my whole code, you can find it on Github: https://github.com/Aemmel/ConsoleGame1
After erasing using iterator it, you cannot use it further as it is invalidated. You should use a result of a call to erase which is new, valid iterator.
for( it = v.begin(); it != v.end();)
{
//...
if(...)
{
it = v.erase( it);
}
else
{
++it;
}
...
}
I fixed the bug with first: making a "simple structure"(struct Entity{int X; intY} and then std::vector [insert name here]) and then with adding an break; if the condition is true.
for (Uint itE = 0; itE < _Enemys.size(); ++itE){
for (Uint it = 0; it<_DestroyedFields.size(); ++it){
if (_Enemys.at(itE).Y == _DestroyedFields.at(it).Y
&& _Enemys.at(itE).X == _DestroyedFields.at(it).X){
_Enemys.erase(_Enemys.begin()+itE);
break;
}
}
}
With struct Position {int x; int y;}; and some utility operators,
you may do one of the following: (https://ideone.com/0aiih0)
void filter(std::vector<Position>& positions, const std::vector<Position>& destroyedFields)
{
for (std::vector<Position>::iterator it = positions.begin(); it != positions.end(); ) {
if (std::find(destroyedFields.begin(), destroyedFields.end(), *it) != destroyedFields.end()) {
it = positions.erase(it);
} else {
++it;
}
}
}
Or, if input are sorted, you may use a 'difference':
std::vector<Position> filter2(const std::vector<Position>& positions, const std::vector<Position>& destroyedFields)
{
std::vector<Position> res;
std::set_difference(positions.begin(), positions.end(),
destroyedFields.begin(), destroyedFields.end(),
std::back_inserter(res));
return res;
}

finding the correct entry in a C++ map using upper and lower bound

I'm trying to optimize my search in a C++ map by using the upper_bound function:
The map is the variable table:
Foo& search(uint64 id, uint64 hash) {
std::map<std::pair<uint64, uint64>, Foo>::const_iterator iter;
std::pair<uint64, uint64> key(id, hash);
iter = table.upper_bound(key);
// for completeness I search for those elements which may be a match
// they may fall into a different hash range
for( ; iter != table.end() || iter != table.begin(); --iter ) {
const Foo foo = iter->second;
if(foo.id() == foo.first_hash() <= hash &&
hash <= foo.second_hash()) {
if( foo.state() == NORMAL) {
return foo;
}
else {
// do something else
}
}
}
However, when I execute the program it just hangs ... it looks like the search is not working at all, and I don't have the logs to tell me where the mistake was ... What am I doing wrong here? When I do a linear search, it works fine, but now when I try to improve the algorithm it fails ...
Your loop condition
for( ; iter != table.end() || iter != table.begin(); --iter )
is the source of your infinite loop, as it is always true.
What you want to do, judging from the comments, is use a reverse iterator:
map<int,int> a;
for (int i = 0; i < 100; i++) {
a[i] = 2*i;
}
auto it = a.upper_bound(5);
reverse_iterator<map<int,int>::iterator> rev_it (it);
for (; rev_it != a.rend(); rev_it++) {
cout << rev_it->first;
}
This will print 543210.