What i am doing wrong in erasing some vector elements? - c++

Here i am trying to print the frequency of each word in the sentence, which is stored in the vector of string
void display_by_word (vector<string> vs) //pass by value is necessary because we need to delete the elements.
{
vector<string> :: size_type vec_size, i;
string to_cmp = vs[0];
int occ = 0;
for ( i = 0; i < vs.size(); ++i){
vector <string> :: iterator it = vs.begin() + 1;
occ = 1;
for ( it ; it != vs.end(); ++it){
if ( vs[i] == *it){
vs.erase(it);
occ++;
}
}
cout << vs[i] << " " << occ << endl;
}
}
Sometimes it works fine but sometimes it crashes.what is wrong?

See http://en.cppreference.com/w/cpp/container/vector/erase
Invalidates iterators and references at or after the point of the erase [...]
After the erase has happened, you cannot reuse it because it has been invalidated. It's undefined behaviour, which can include random crashes.
However, erase returns an iterator to the element following the erased one, or to end() if it was the last element, which is why the solution with it = vs.erase(it); works.
Alternatively, consider using std::remove_if, followed by the two-argument erase, which is known as the Erase-Remove Idiom. It may turn out to be more elegant and more readable than a hand-written loop. Or just rewrite the whole function to use std::count_if.

You may want to rewrite the loop as something like this
while(it != vs.end())
{
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else
it++;
}

You may should do as below:
`
for ( it ; it != vs.end(); ){
if ( vs[i] == *it){
it = vs.erase(it);
occ++;
}
else{
++it;
}
}
`

Related

memory out of range Vector [duplicate]

This question already has answers here:
Removing item from vector while iterating?
(8 answers)
Closed 2 years ago.
I'm writing this program why it throws an error in toupper('a')?
void test2(void) {
string n;
vector<string> v;
auto it = v.begin();
do {
cout << "Enter a name of a fruit: ";
cin >> n;
v.push_back(n);
} while (n != "Quit");
v.erase(v.end() - 1);
sort(v.begin(), v.end(), [](string g, string l) { return g < l; });
dis(v);
for (auto i : v) {
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i));
}
}
dis(v);
}
Can someone help me to find the error?
Since you already tried to implement the erase-remove-idiom, that's how it can be used in this case:
v.erase(std::remove_if(v.begin(), v.end(), [](const std::string &item) {
return std::toupper(item.at(0)) == 'A';
}), v.end());
Here I assumed, that i.at(0) == toupper('a') is a typo and should be toupper(i.at(0)) == 'A'.
Write your deletion loop like this:
for ( auto it = std::begin( v ); it != std::end( v ); )
{
if ( toupper( it->at( 0 ) ) == 'A' )
it = v.erase( it );
else
++it;
}
If you do it the way you're doing it you'll invalidate the iterator and then never reassign it a valid iterator which is needed to correctly loop through the vector.
The problem here :
for (auto i : v) {
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i));
}
}
is that you are modifying the vector inside the loop with erase() which invalidates the iterators used internally to implement the for range loop.
The loop is a syntactic sugar for something like this :
{
auto&& range = v;
auto&& first = std::begin(v); // obtained once before entering the loop
auto&& last = std::end(v); // obtained once before entering the loop
for (; first != last; ++first)
{
auto i = *first; // first will be invalid the next time after you call erase()
if (i.at(0) == toupper('a')) {
cout << i << endl;
v.erase(remove(v.begin(), v.end(), i)); // you are invalidating the iterators and then dereferencing `first` iterator at the beginning of the next cycle of the loop
}
}
}
Why calling erase() invalidates the vector ?
This is because a vector is like a dynamic array which stores its capacity (whole array size) and size (current elements count), and iterators are like pointers which point to elements in this array
So when erase() is called it will rearrange the array and decrease its size, so updating the end iterator and your first iterator will not be pointing to the next item in the array as you intended . This illustrates the problem :
std::string* arr = new std::string[4];
std::string* first = arr;
std::string* last = arr + 3;
void erase(std::string* it)
{
std::destroy_at(it);
}
for (; first != last; ++first)
{
if (some_condition)
erase(first); // the last element in the array now is invalid
// thus the array length is now considered 3 not 4
// and the last iterator should now be arr + 2
// so you will be dereferencing a destoryed element since you didn't update your last iterator
}
What to learn from this ?
Never do something which invalidates the iterators inside for range loop.
Solution:
Update iterators at each cycle so you always have the correct bounds :
auto&& range = v;
auto&& first = std::begin(v); // obtained once before entering the loop
auto&& last = std::end(v); // obtained once before entering the loop
for (; first != last;)
{
auto i = *first;
if (i.at(0) == toupper('a'))
{
first = v.erase(remove(v.begin(), v.end(), i));
last = std::end(v);
}
else
{
++first;
}
}

loop hangs for iterated std::erase on std::list

I'm trying to remove duplicate combinations of integer vectors stored in a list using a hash table. Iterating over each integer vector in the list, I:
Calculate the hash_value (thash)
See if the hash value is already in the hash table (pids)
If it's in the hash table, erase that vector from the list.
Otherwise, add that value to the hash_table and increment the list
iterator
Print statements seem to confirm my logic, but the loop hangs at the fourth step of iteration. I've commented the it++ and vz.remove(it) that cause the problem and only show the logic in the code below. The code is also available through ideone: https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}
Problem is this line of code:
vz.erase(it);
It keeps iterator where it was ie leaves it invalid. It should be either:
vz.erase(it++);
or
it = vz.erase( it );
Note: std::unoredered_set::insert() return value tells you if insert was succesfull or not (if the same value element is there already), you should call it and check result. In your code you do lookup twice:
if (pids.insert(thash).second ) {
// new element added
++it;
} else {
// insertion failed, remove
it = vz.erase( it );
}
As std::list provides remove_if() your code can be simplified:
vz.remove_if( [&pids,&lprimes]( auto &v ) {
return !pids.insert( hash_cz(v, lprimes) ).second );
} );
instead of whole loop.
If the element has already been seen, you erase() the it node and then increment it at the end of the loop: undefined behaviour. Try erase(it++) instead.
If the element has not been seen, you increment it and then do it again at the end of for, yielding UB if it was end() - 1 as it moves past end.

Iterating multimap with conditional statement

First of all, I'm using C++98/03
I'm iterating my multimap starting from the second element:
multimap<pair<string, string>, pair<string, int> >::iterator it = paths.begin();
it++;
I have a conditional statement: if first element of first pair in current iterator is equal to the first element of first pair in a previous iterator, then do something, eg. print these elements.
for(; it != paths.end(); it++) {
if((*it).first.first == (*it--).first.first ) {
it++;
cout << (*it).first.first << " ";
cout << (*it--).first.first << endl;
it++;
}
else {
it++;
}
}
My question is how can I use a copy of an iterator instead of incrementing it back after every (*it--)?
Create an utility similar to C++11's std::prev:
#include <algorithm>
template <class T>
T prev(T it)
{
std::advance(it, -1);
return it;
}
Then use it as follows:
for(; it != paths.end(); it++) {
if((*it).first.first == prev(it)->first.first ) {
cout << (*it).first.first << " ";
cout << prev(it)->first.first << endl;
}
else {
it++;
}
}
Just use another iterator:
typedef multimap<pair<string, string>, pair<string, int> >::iterator iterator;
for( iterator it = paths.begin(); it != paths.end(); ) {
iterator prev = it++;
if( it == paths.end() )
break;
if( prev->first.first == it->first.first ) {
// output here
}
}
Note your code is incorrect, first of all it has UB as == is not sequenced. But even if you use different iterator on the left side, you would get wrong behaviour:
iterator it1 = it;
if((*it1).first.first == (*it--).first.first ) { // not UB anymore, but result is always true as you compare the same element

Better way to iterate std::map

Given a map, I need to retrieve and operate two immediately stored items.
To me, working on a vector is litter easier since I can do "iter + 1" or "iter - 1".
While for map, I am out of luck.
For example, I give a simple example as follows:
Note: in my real application, I don't simply subtract those numbers.
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
map<char,int>::iterator firstItem = mymap.begin();
map<char,int>::iterator secondItem = ++mymap.begin();
for ( ; secondItem != mymap.end(); ++firstItem, ++secondItem )
cout << secondItem->second - firstItem->second << endl;
return 0;
}
Question> Is there a better solution for this?
Thank you
Instead of incrementing both iterators in the loop control (incrementing is a bit slow), just assign firstItem = secondItem then increment secondItem.
You can do it with a single iterator. Move the increment from the header to the middle of your loop, and exit the loop when you hit the end of your map, like this:
map<char,int>::iterator item = mymap.begin();
for (;;) {
int first = item->second;
++item;
if ( item == mymap.end()) break;
cout << item->second - first << endl;
}
This is a matter of style. You can do eg.
auto first = m.begin();
if (first != m.end())
{
auto second = first;
second++;
for (; second != m.end(); first = second++)
{
...
}
}
You can also bailout more elegantly in the case where the map is empty. For instance you can do:
if (m.empty()) return;
auto first = m.begin(), second = first;
for (second++; second != m.end(); first = second++)
{
...
}
I'd favor the latter if I can, and use the former only if I must.
Your current loop will show undefined behaviour if the map is empty.
Your loop could be rewritten (more simply, and checking for an empty map) like so:
int main(int argc, char * argv[])
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
for ( it = ( mymap.begin() == mymap.end() ? mymap.end() : std::next(mymap.begin()) ) ; it != mymap.end(); ++it )
cout << it->second - std::prev(it)->second << endl;
return 0;
}
Your code will have undefined behavior if the map is empty but other than that it seems to be a reasonable approach, depending on your overall goal. Since map iterators are not random access you can't just add or subtract one, only increment/decrement.
An alternate approach is to make a copy of the iterator and then incrementing inside the loop.
Neither better, nor worse, just an alternative:
if (map.size() >=2)
std::accumulate(
++mymap.begin(),
mymap.end(),
mymap.begin(),
[](mymap_type::const_iterator iprev, mymap_type::value_type const& entry)->mymap_type::const_iterator
{
/* do something */;
return ++iprev;
});

How do you determine the last valid element in a STL-Container

If i iterate over a STL container i sometimes need to know if the current item is the last one in the sequence. Is there a better way, then doing something like this? Can i somehow convert rbegin()?
std::vector<int> myList;
// ....
std::vector<int>::iterator lastit = myList.end();
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != myList.end(); it++) {
if(it == lastit)
{
// Do something with last element
}
else
{
// Do something with all other elements
}
Try the following
std::vector<int>::iterator it2 = (++it);
if ( it2 == myList.end() ) {
...
}
The following should work as well
if ( it+1 == myList.end() ) {
// it is last
...
}
Maybe you can iterate backwards (use rbegin/rend) and put your special task before the loop or replace the end check with it != lastit and put the special handling after the loop
I would have some doubts about my design if some elements need to be treated differntly, but this suggestion is a bit cleaner for me (don't forget to test for empty containers)
std::vector<int>::iterator lastit = myList.end();
if (lastit != myList.begin())
{
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != lastit; ++it)
{
// Do
}
// Do with last
}
Use reversed iteration, this way you will have only one end()-1-like computation (notice the rbegin()+1) and no comparsions:
for(vector<int>::iterator it = myValues.rbegin()+1; it != myValues.rend(); it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
Also, for the vector<>, computing end()-1 is probably fast, so you can also do it like following:
for(vector<int>::iterator it = myValues.begin(); it != myValues.end()-1; it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
If you don't want to process the element after the loop, you can:
for(vector<int>::iterator it = myValues.rbegin(); it != myValues.rend(); it++) {
if(it == myValues.rbegin())
cout << "Process last one: " << *it << endl;
else
cout << *it << endl;
}
For a random access iterator like that for vector, you don't need the temporarary. You can say:
if ( it + 1 == v.end() ) {
// at one before end
}
Edit: And even for non-random access types one could use std:;distance:
if ( distance( it, v.end() ) == 1 ) {
// at one before end
}
An important question is: why create a loop if you do something special for 1 element. Why not do something special to the 3rd element? To every 4rth? ...
Just iterate over the elements to be treated the same, write separate code to treat the others.
Have a look at answers to this question, too.
Why not:
if(!myList.empty())
last_it = myList.begin() + myList.size()-1;
else
last_it = myList.end();
//or
last_it = myList.empty() ? myList.end() : myList.begin() + myList.size() - 1;
If you're using a vector, it's actually much simpler to use an integer index to iterate:
std::vector<int> myList;
for (unsigned int i = 0; i < myList.size(); i++)
{
if (i == (myList.size() - 1))
{
processDifferently (myList[i])
}
else
{
process (myList[i])
}
}
Minimizing the number of calls to myList.size() is left as an exercise for the OP :)