C++ remove_if overwriting my vector - c++

My remove_if seems to be overwriting the elements that are not filtered out with values of filtered out elements. The purpose of these code is to allow user to filter and display only teacher from a certain category. (Not deleting any element)
Here are some of the code
static string compare;
static string debug;
bool filter_Cat (Teacher &t)
{
return (t.getCat() != compare);
}
void filterCat (vector<Teacher> &t)
{
vector<Teacher>::iterator i;
vector<Teacher>::iterator newedited = remove_if(t.begin(), t.end(), filter_Cat);
for (i = t.begin(); i != newedited; ++i)
{
Teacher& te = *i;
te.getName();
cout << "\t";
te.getCategory();
cout << "\t";
te.getLocation();
}
}
void filterTutorCat(vector<Teacher> &t)
{
int choice;
cout << "No\tCategory" << endl
<< "1\tEnglish" << endl
<< "2\tMath" << endl
<< "3\tScience" << endl
<< "Choose the category you wish to filter :";
cin >> choice;
getline(cin, debug);
if(choice <= 3 && choice > 0)
{
if (choice == 1)
{
compare = "English";
filterCat(t);
}
if (choice == 2)
{
compare = "Math";
filterCat(t);
}
if (choice == 3)
{
compare = "Science";
filterCat(t);
}
}
else
{
cout << "Invalid Option" << endl;
}
}

remove_if shifts elements, for which the compare function returns false, from right to left; which in other words means, it overwrites the elements, for which compare returns true, with elements, for which compare returns false. The size of the vector doesn't change, however.
This reads,
Removes all elements satisfying specific criteria from the range [first, last). The first version removes all elements that are equal to value, the second version removes all elements for which predicate p returns true.
Removing is done by shifting the elements in the range in such a way that elements to be erased are overwritten. The elements between the old and the new ends of the range have unspecified values. Iterator to the new end of the range is returned. Relative order of the elements that remain is preserved.
So what you want to do should be expressed as:
void filterCat (vector<Teacher> &v)
{
for (vector<Teacher>::iterator it = v.begin(); it != v.end() ; ++it)
{
if (!filter_Cat(*i))
{
std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
}
}
}
It seems in your code, getName() prints the name which ideally it should not do, instead it should return name. So I would suggest you to change it to make it return name. And do the same for getCategory as well. Choose your name correctly. If it is getName(), you should get you name by returning it; if it is printName(), then it should print name.
Also, the code which you've written isn't good:
You should avoid global variables.
You should avoid if-else as much as possible. Learn better ways.
You should learn about function objects (or functor)
You should learn about const member function.
You should understand the difference between iterator and const_iterator, and their usage.
You should understand the difference between const reference, and non-const reference. And try using them appropriately.
So I would write your code as:
//this is functor, not a function
struct filter_cat
{
std::string m_cat; //use member data, avoid global variable
filter_cat(std::string const & cat) : m_cat(cat) {}
bool operator()(Teacher const & t) const //const member function
{
return (t.getCat() != m_cat); //getCat should be const member function
}
};
//pass vector by const reference
void filterCat (vector<Teacher> const & v, filter_cat filter)
{
//use const_iterator here, instead of iterator
for (vector<Teacher>::const_iterator it = v.begin(); it != v.end() ; ++it)
{
if (!filter(*i))
{
//getName and getCategory should be const member function
std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
}
}
}
void filterTutorCat(vector<Teacher> const &t)
{
int choice;
cout << "No\tCategory" << endl
<< "1\tEnglish" << endl
<< "2\tMath" << endl
<< "3\tScience" << endl
<< "Choose the category you wish to filter :";
cin >> choice;
getline(cin, debug);
//avoid if-else as much as possible, learn better ways!
std::string cats[] = {"English", "Math", "Science"};
if(choice <= 3 && choice > 0)
{
filterCat(v, filter_cat(cats[choice-1]));
}
else
{
cout << "Invalid Option" << endl;
}
}
As noted in the comments: getCat, getName and getCategory should be const member functions. In fact, if getCategory returns category, then getCat isn't even needed.
Solved my issue.

remove_if collects the values for which filter_Cat returns false at the start of the container. While it doesn't reduce the number of elements in the container it neither does make any guarantees about the values of the elements beyond the returned range. So you are loosing values when using remove_if.

Related

How to remove only one element from a vector with duplicates

I am making a D&D game in C++. I roll 6 scores randomly, put them in a vector, display them to the player. Then I go through each ability (str, dex, con, int, wis, and cha), and call a function that asks the player which of the scores they want to use for each ability, and then i remove it from the vector, return the value, and move on to the next ability. It works fine unless there is a duplicate roll, in which case it deletes both of the duplicates. I want it to only remove one at a time regardless of duplicates, and I haven't been able to find anything online to do this. Here is the function call
int Character::initScores(std::vector<int> & v, std::string ability)
{
int c = 0;
bool error = 0;
do {
if (c != 0) {
std::cout << "That isn't one of your scores. Try again. " <<
std::endl;
}
int choice;
std::cout << ability << ": ";
std::cin >> choice;
if (std::find(v.begin(), v.end(), choice) != v.end())
{
v.erase(std::remove(v.begin(), v.end(), choice), v.end());
std::cout << "Your remaining rolls are ";
for (int i = 0; i < v.size(); i++)
std::cout << v[i] << " ";
std::cout << std::endl;
return choice;
}
else
{
c++;
error = 1;
}
} while (error = 1);
}
And the function calls
std::cout << "Enter which score you want for... " << std::endl;
strength = initScores(scores, "Strength");
dexterity = initScores(scores, "Dexterity");
constitution = initScores(scores, "Constitution");
intelligence = initScores(scores, "Intelligence");
wisdom = initScores(scores, "Wisdom");
charisma = initScores(scores, "Charisma");
Also please lmk if there is anything inefficient/bad practice in my code, I have only recently started working on my own coding projects
You are calling std::remove(), which "removes" ALL matching values from the container (really, it just moves them to the end of the container), and then you are calling the 2-parameter overload of the erase() method to physically delete ALL of the "removed" values from the container.
If you just want to remove 1 element, pass the iterator returned by std::find() to the 1-parameter overload of the erase() method:
auto iter = std::find(v.begin(), v.end(), choice);
if (iter != v.end())
{
v.erase(iter);
...
}

How to compare elements of a vector against each other? C++

I am supposed to read some data (specifically string datatype) and store each element in a vector. Now I have to check if any of the different strings that were inputted match in size, and if they do I have to see if there are any matching letters. Now my question is how do I compare what's inside the vector (first the size and then the different letters). Is it possible?
Say I have
HELLO
and
HELLA
They have the same size, and 4 letters that match.
This is what I am trying to accomplish.
The code that I have does not work given my ignorance about the matter.
Thank you in advance.
vector <string> myVector;
//insert data insdide of the vector
myVector.push_back("Hello");
myVector.push_back("Hello");
myVector.push_back("Hello2");
myVector.push_back("Hello3");
myVector.push_back("Hello4");
//This is wrong
for (unsigned int i = 0; i < myVector.size(); i++) {
if (myVector[i].size == myVector[i+1].size()){
cout << "SAME SIZE" << endl;
}
}
return 0;
You just have make a simple mistake for size() function and you are trying to access the element which is not present by using i+1 for last iteration.
So just change your for loop just as below
for (unsigned int i = 1; i < myVector.size(); i++)
{
if (myVector[i].size() == myVector[i-1].size()) // .size() should be used
{
cout << "SAME SIZE" << endl;
}
}
Here's a way of writing it:
// returns true if #param s1 and #param s2 are equal in letters
bool isEqual(const string& s1, const string& s2) {
if(s1.size() != s2.size())
return false;
bool equal = false;
// iterates over all the characters in s1 and s2 and compare them
for(auto ch1 = s1.cbegin(), ch2 = s2.cbegin(); ch1 != s1.cend(),ch2!= s2.cend(); ch1++, ch2++) {
if(*ch1 == *ch2)
equal = true;
else
return false;
}
return equal;
}
// type of iter is vector<string>::const_iterator meaning it can only read the value
for (auto iter = myVector.cbegin(); iter != myVector.cend() - 1; iter++){
if(isEqual(*iter, *(iter + 1)))
std::cout << *iter << " equal " << *(iter + 1) << endl;
else
std::cout << *iter << " different " << *(iter + 1) << endl;
}
Here, I used iterators(you should write code in modern C++, avoid using subscript).

Erasing pointers from vector while iterating another vector

I have tried many other similar questions but none of them helped me. My problem is as following:
I have 3 vectors of pointers to my struct: vector<state*>where state is my kind of struct. What I am trying to do is to remove states from vectorCheck if they are in either vectorOpen or vectorClosed. The point is, it sometimes works fine and sometimes not. According to CodeBlocks this seems to be a problem but I have no idea to overcome this. I debugged my program step by step and at some point, state from vectorCheck is not being removed despite of the fact it is in vectorClosed.
Iterating is held by 2 for loops:
vector<state*> vectorOpen;
vector<state*>::iterator itOpen;
vector<state*> vectorClosed;
vector<state*>::iterator itClosed;
vector<state*> vectorCheck;
vector<state*>::iterator itCheck;
for(itCheck = vectorCheck.begin(); itCheck != vectorCheck.end(); itCheck++) {
for(itOpen = vectorOpen.begin(); itOpen != vectorOpen.end(); itOpen++) {
if ((*itCheck)->player->x == (*itOpen)->player->x &&
(*itCheck)->player->y == (*itOpen)->player->y &&
(*itCheck)->box[0].x == (*itOpen)->box[0].x &&
(*itCheck)->box[0].y == (*itOpen)->box[0].y) {
cout << "erasing as in open " << (*itCheck)->player->x << " " << (*itCheck)->player->y << " " << (*itCheck)->box[0].x << " " << (*itCheck)->box[0].y << endl;
vectorCheck.erase(itCheck);
}
}
}
for(itCheck = vectorCheck.begin(); itCheck != vectorCheck.end(); itCheck++) {
for(itClosed = vectorClosed.begin(); itClosed != vectorClosed.end(); itClosed++) {
if((*itCheck)->player->x == (*itClosed)->player->x &&
(*itCheck)->player->y == (*itClosed)->player->y &&
(*itCheck)->box[0].x == (*itClosed)->box[0].x &&
(*itCheck)->box[0].y == (*itClosed)->box[0].y) {
cout << "erasing as in closed " << (*itCheck)->player->x << " " << (*itCheck)->player->y << " " << (*itCheck)->box[0].x << " " << (*itCheck)->box[0].y << endl;
vectorCheck.erase(itCheck);
}
}
}
Where vectorCheck is a maximum size of 3. To explain what I mean here is the picture
Where I am talking here about states in green rectangulars (3 1 2 4). Why isn't it being removed like the state in blue rectangular (2 2 2 4)? It should be removed as this state has appeared already in vectorClosed (code above).
What am I doing wrong? This is not the first iteration of the program, it happens in like 6th or 7th loop.
Also, this is probably causing my program to crash later on.
As mentioned in my comment, the problem is that you are continuing to use an iterator to an element you erased. std::vector::erase(i) is invalidating the itCheck iterator.
We can fix this by taking advantage of C++ algorithms like std::remove_if. It may make the code appear more complex at first glance, but you'll find this style of coding lets you reuse pieces of logic, improving the readability and maintainability of your code.
To start, let's write a functor that does the equality comparison you need.
struct states_are_equal :
public std::binary_function<state const *, state const *, bool>
{
bool operator()(state const * a, state const * b) const {
return a->player->x == b->player->x &&
a->player->y == b->player->y &&
a->box[0].x == b->box[0].x &&
a->box[0].y == b->box[0].y;
}
};
Now we need a predicate that will return true if the given item is found within another container. This part admittedly may be a bit hard to follow if you are not familiar with the algorithms library.
template <typename Iterator, typename Comparer>
struct is_in_container_func :
public std::unary_function<
typename std::iterator_traits<Iterator>::value_type const &,
bool
>
{
is_in_container_func(Iterator begin, Iterator end, Comparer cmp)
: it_begin(begin), it_end(end), comparer(cmp) { }
bool operator()(argument_type i) const {
return std::find_if(it_begin, it_end, std::bind1st(comparer, i)) != it_end;
}
private:
Iterator it_begin;
Iterator it_end;
Comparer comparer;
};
// This is just a helper to allow template type deduction; its only purpose is to
// allow us to omit the types for Iterator and Comparer when constructing an
// is_in_container_func object.
template <typename Iterator, typename Comparer>
is_in_container_func<Iterator, Comparer> is_in_container(
Iterator begin, Iterator end, Comparer cmp)
{
return is_in_container_func<Iterator, Comparer>(begin, end, cmp);
}
Now we can put all of these pieces together with std::remove_if:
std::vector<state*> vectorOpen;
std::vector<state*> vectorClosed;
std::vector<state*> vectorCheck;
// Make one pass, removing elements if they are found in vectorOpen.
std::vector<state*>::iterator new_end = std::remove_if(
vectorCheck.begin(), vectorCheck.end(),
is_in_container(vectorOpen.begin(), vectorOpen.end(), states_are_equal()));
// Make another pass, removing elements if they are found in vectorClosed.
new_end = std::remove_if(
vectorCheck.begin(), new_end,
is_in_container(vectorClosed.begin(), vectorClosed.end(), states_are_equal()));
// std::remove_if just swaps elements around so that the elements to be removed are
// all together at the end of the vector, and new_end is an iterator to the first
// one. So, finally, we just need to remove the range [new_end, end()).
vectorCheck.erase(new_end, vectorCheck.end());
The erase call invalidates the iterator passed to it. It shifts the elements in the vector one place to their left, and returns an iterator to the element after the removed one. Therefore, you should not increment the iterator if the erase was executed. Like so:
for(itCheck = vectorCheck.begin(); itCheck != vectorCheck.end();) { // no increment
bool found = false;
for(itOpen = vectorOpen.begin(); itOpen != vectorOpen.end(); itOpen++) {
if ((*itCheck)->player->x == (*itOpen)->player->x &&
(*itCheck)->player->y == (*itOpen)->player->y &&
(*itCheck)->box[0].x == (*itOpen)->box[0].x &&
(*itCheck)->box[0].y == (*itOpen)->box[0].y) {
cout << "erasing as in open " << (*itCheck)->player->x << " " << (*itCheck)->player->y << " " << (*itCheck)->box[0].x << " " << (*itCheck)->box[0].y << endl;
itCheck = vectorCheck.erase(itCheck);
found = true;
break; // found element and erased it. back to outer loop
}
}
if (!found) ++itCheck; // didn't find it, need to increment
}

multimap iterator not working

I have a Playlist class that has a vector with Tracks and each Track has a multimap<long, Note> as datamember.
class Track {
private:
multimap<long, Note> noteList;
}
Using an iterator to acces the tracks is no problem, so this part here is working fine:
vector<Track>::iterator trackIT;
try{
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}catch (int e){
cout << "exception #" << e << endl;
}
What I want to do next is iterate the Notes of each Track. But starting from this part all output is stopped. So I only get to see the first tracks name. Any cout's after that are not shown and the compiler isn't giving me any errors. Even the cout inside the try catch block isn't working..
vector<Track>::iterator trackIT;
multimap<long, Note>::iterator noteIT;
for(trackIT = this->playlist.getTracklist().begin(); trackIT < this->playlist.getTracklist().end(); trackIT++){
cout << trackIT->getTrackName() << endl;
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}
cout << "random cout that is NOT shown" << endl; // this part doesn't show up in console either
Also, the method in my Track class that I'm using to add the Note objects looks like this:
void Track::addNote(Note &note) {
long key = 1000009;
this->noteList.insert(make_pair(key, note));
}
// I'm adding the notes to the track like this:
Note note1(440, 100, 8, 1, 1);
note1.setName("note1");
synthTrack.addNote(note1);
Any ideas why the iterator won't work?
Change
noteIT < trackIT->getNoteList().end()
To
noteIT != trackIT->getNoteList().end()
Not all iterators support less than / greater than comparisons.
If you have c++11 you can use a range-based for loop:
for (Note& note : trackIT->getNoteList())
Or you can use BOOST_FOREACH
BOOST_FOREACH (Note& note, trackIT->getNoteList())
You haven't shown the definitions of getTrackList or getNoteList, but there's a common mistake people make - if you return a copy of the container instead of a reference to it, the iterators will be pointing to different containers making comparisons impossible. Not only that but since the containers are temporary any use of the iterators results in undefined behavior.
If you are really hardcoding the track key, then there will only ever be one track in the map because std::map stores unique keys...
long key = 1000009; //If yo are really doing this, this key is already inserted so it will fail to insert more.
Also, if you would like a more elegant approach you could use function object.
struct print_track
{
void operator()(const Track& track)
{
cout << track.getTrackName() << endl;
std::for_each(track.getNoteList().begin(), track.getNoteList().end(), print_track_name());
}
};
struct print_note_name
{
void operator()(const std::pair<long,Note>& note_pair)
{
cout << "---" << note_pair.second.getName() << endl;
}
};
//In use...
std::for_each(playlist.getTracklist().begin(), playlist.getTracklist.end(), print_track());

C++: STL linked list - += duplicating node

I am working on a Polynomial class which uses the STL linked list. One of the functions requires me to add two Polynomial's together. For some reason, the += operator seems to be duplicating the node, as opposed to merely modifying the contents.
Here is the class declaration:
class Polynomial
{
public:
Polynomial(pair<double,int>); //Specified constructor
void add(const Polynomial&);
void print();
private:
Polynomial(); //Default constructor
list<pair<double,int> > terms;
};
This is the add member function:
void Polynomial::add(const Polynomial& rhs)
{
list<pair<double,int> >::const_iterator r;
list<pair<double,int> >::iterator l;
for(r=rhs.terms.begin(); r!=rhs.terms.end(); r++)
{
bool match=0;
//Check to see if we have an existing nth order node
for(l=terms.begin(); l!=terms.end(); l++)
{
//If we do, just add the coefficients together
if(l->second == r->second)
{
l->first += r->first;
match = 1;
}
}
//If there was no matching existing node, we need to find out
//where to insert it into the list.
if(!match)
{
l=terms.begin();
bool inserted=0; //Sentinel for the loop
while(l!=terms.end() && !inserted)
{
//If there's only one term in the list
//Just compare and stick it in front or behind the existing node
if(terms.size()==1)
{
int this_exp = l->second;
int exp_to_ins = r->second;
if(exp_to_ins > this_exp) terms.push_back((*r));
if(exp_to_ins < this_exp) terms.push_front((*r));
inserted = 1;
}
//If there's more than one node, we need to traverse the list
if(terms.size()>1)
{
if(l!=terms.begin())
{
int this_exp = l->second;
l++;
int next_exp = l->second;
int exp_to_ins = r->second;
//If the new node value is between the current and next node
//Insert between them.
if((this_exp < exp_to_ins) && (exp_to_ins < next_exp))
{
terms.insert(l,(*r));
inserted = 1;
}
}
else if(l==terms.begin())
{
int this_exp = l->second;
int exp_to_ins = r->second;
//This will be the smallest order node
//Put it in the top spot
if(this_exp > exp_to_ins)
{
terms.push_front((*r));
inserted = 1;
}
l++;
}
}
}
//If we've traversed the list and can't find the right place
//this must be the greatest order node in the list
//so just tack it on the end.
if(!inserted) terms.push_back((*r));
}
}
}
Works fine with ordering the nodes in the correct order, but we have an existing nth order node, rather than just adding the coefficients together, it keeps the original node but seems to make a second node with the coefficients added together, and I have no idea why.
If I run the print function, for what should be F(x) = -2x^7 + 3x^6 - 11x^5 - 2x^4, instead I get F(x) = -2x^7 + 3x^6 - 11x^5 - 10x^5. If I call the size() function on the list, I get 4. But if I run the following code to print out the info from the nodes in the list:
stringstream test;
for(i=terms.end(); i!=terms.begin(); i--)
{
test << "Coefficient: " << i->first << " ";
test << "Exp: " << i->second << endl;
}
cout << "Size: " << terms.size() << endl;
cout << test.str();
The following is output:
Coefficient: -10 Exp: 5
Coefficient: -2 Exp: 7
Coefficient: 3 Exp: 6
Coefficient: -11 Exp: 5
Any help greatly appreciated.
EDIT: This is the test program.
Polynomial p(pair<double, int>(-10, 5));
p.add(Polynomial(pair<double,int> (-2,4)));
p.add(Polynomial(pair<double,int> (3,6)));
p.add(Polynomial(pair<double,int> (-2,7)));
p.add(Polynomial(pair<double, int> (-1,5)));
Your add() function seems to be correct except the print:
for(i=terms.end(); i!=terms.begin(); i--)
{
test << "Coefficient: " << i->first << " ";
test << "Exp: " << i->second << endl;
}
This is completely wrong, and invokes undefined behavior. i is initially terms.end() and you've dereferencing it? items.end() returns past-the-end iterator. Even if I assume it correct for a while, the condition i!=terms.begin() means the first element is never printed!
So the fix is this:
for(list<pair<double,int> >::iterator i=terms.begin(); i!=terms.end(); i++)
{
test << "Coefficient: " << i->first << " ";
test << "Exp: " << i->second << endl;
}
And it prints expected output:
Size: 4
Coefficient: -2 Exp: 4
Coefficient: -11 Exp: 5
Coefficient: 3 Exp: 6
Coefficient: -2 Exp: 7
Is it not correct?
See the output yourself here also : http://www.ideone.com/p8mwJ
By the way, instead of add, you could make it operator+= instead, as:
const Polynomial& operator+=(const Polynomial& rhs)
{
//same code as before
return *this;
}
If you write so, then you can add polynomials as:
Polynomial p(pair<double, int>(-10, 5));
p += Polynomial(pair<double,int> (-2,4));
p += Polynomial(pair<double,int> (3,6));
p += Polynomial(pair<double,int> (-2,7));
p += Polynomial(pair<double, int> (-1,5));
Demo : http://www.ideone.com/aA1zF
I just read your comment, and came to know that you want to print it in reverse order, in that case, you could use rbegin() and rend() instead of begin() and end() as:
for(list<pair<double,int> >::const_reverse_iterator i=terms.rbegin();
i!=terms.rend();
i++)
{
test << "Coefficient: " << i->first << " ";
test << "Exp: " << i->second << endl;
}
I would also advice you to make print a const function as :
void print() const
//^^^^ this makes the function const!
Better yet overload operator<< .
Anyway reverse order printing demo : http://www.ideone.com/Vk6XB
Your test loop (the one printing in the stringstream) is incorrect: it's undefined behavior to dereference the end () iterator. Probably your "std::list" is implemented in a circular way (i.e. with begin == end+1) so dereferencing "end" gives you *begin in your test loop.
Use reverse iterators to print the list in reverse order:
for (i = list.rbegin (); i != list.rend (); ++i)
{
test << "Coefficient: " << i->first ; // etc.
}
Besides the problem pointed out by #Nawaz, there is also a problem in the Polynomial::add function.
If the if(terms.size()==1) block is executed, a new item is inserted in the list. But that increases the size of the list by one, so the if(terms.size()>1) block will also be executed. And this can insert the same node once more.
A bit further in the while loop, you increment l, and proceed using the next node, without checking whether it's valid (ie. without comparing to terms.end()).
There might be more such mistakes, but these came up after a cursory glance.