Delete Elements from Vector Inside Loop - c++

I have a vector, words_in_family, of type: vector<vector<string>>. I am trying to delete every element of words_in_family that is not equal to the string vector largest_family, but am having issues and am unsure of why. Any help is appreciated.
for (int i = words_in_family.size() - 1; i >= 0; i--)
{
if (words_in_family[i] != largest_family)
{
words_in_family.erase(words_in_family[i]);
}
}

erase method doesn't take value. Instead use iterator as parameter for erase method:
vector<vector<string>>::iterator it = words_in_family.begin();
for (int i = words_in_family.size() - 1; i >= 0; i--)
{
if (words_in_family[i] != largest_family)
{
words_in_family.erase(it+i);
}
}
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
Note that the iterators for std::vector are random access iterators so you can add/subtract integral values to get other valid iterators.

C++ std::vector erase an element, in your case:
Erase an element from a vector(words_in_family) whose value is equal to largest_family:
std::vector<std::vector<std::string>>::iterator Itr;
for(Itr = Words_in_family.begin(); Itr != Words_in_family.end();)
{
if(*Itr == largest_family)
{
Itr = Words_in_family.erase(Itr);
}
else
{
Itr++;
}
}

words_in_family.erase(words_in_family.begin()+i, words_in_family.begin()+i+1);
erase takes iterator as an argument

Related

Iterators over two-dimensional vectors

I have a function that will remove the duplicates in an unsorted vector, keeping it unsorted:
int unsortedRemoveDuplicates(std::vector<int>& numbers)
{
std::set<int> seenNums; //log(n) existence check
auto itr = begin(numbers);
while(itr != end(numbers))
{
if(seenNums.find(*itr) != end(seenNums)) //seen? erase it
itr = numbers.erase(itr); //itr now points to next element
else
{
seenNums.insert(*itr);
itr++;
}
}
return seenNums.size();
}
However I wish to use this for a two-dimensional vector initialised as
vector<vector<int>> numbers;
and have the function remove duplicates from only one row determined by a row number passed into the function.
I've tried changing the iterator itr to
auto itr = begin(numbers[row]);
while(itr != end(numbers[row]))....
where row is pass into the function, but this only creates a seg fault, and so I'm stuck here getting it to iterate over one row. Any suggestions?
Don't over think. Just iterate the outer vector and pass each inner vector to the function.
for (auto& each_row : numbers)
unsortedRemoveDuplicates(each_row);

Does iteration go through the whole list

Take this code:
std::list<int> intList;
for (int i = 0; i < 10; ++i) {
intList.push_back( 1 << i );
}
std::list<int>::const_iterator iterator;
for (iterator = intList.begin(); iterator != intList.end(); ++iterator) {
std::cout << *iterator;
}
I see how to iterate through a list. Looking at the iteration I think you skip the last item. Is this the case and if so what is the best way to solve it.
Actually, the last item is not skipped. The iterator pointing to intList.end()-1 points to the last item instead of intList.end() as you may be thinking.

Vector iterator invalidation

I am having an issue and I think it is because of the iterators being invalidated. However I use the iterator from erase() to resume iterating other the structure. When erase() when I try to increment after erase() is called the first time I get the following error
'vector iterator not incrementable '
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
}
}
}
Can anyone see where I am going wrong?
erase returns an iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
so if you erase you do not need to increment your iterator
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); )
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
else
{
++itVector
}
}
This solved my issue, I just have to call break after i erase but once i erase i do not need to loop to the end of the list. (#Aleexander solution also works)
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
break;
}
}
}
}

Can I reuse an invalidated iterator?

The following code works (and admittedly is not the most efficient way to go about this routine). My question is this, is it discouraged to reuse the iterator as I have done here? Might it produce strange behavior? If so, why?
std::map<char, int> map;
map['a'] = 10;
map['b'] = 30;
map['c'] = 50;
map['d'] = 70;
std::map<char, int>::iterator iterator = map.begin();
for (; iterator != map.end(); iterator++) {
if (iterator->second == 30 || iterator->second == 50) {
map.erase(iterator);
iterator = map.begin();
}
}
No, there is nothing wrong with re-assigning to the iterator and reusing it, because after the assignment operator is run, the old value is completely overwritten.
iterator = map.begin();
You're not using an invalidated iterator, but your logic is flawed. To fix it, make a small change to your code; only increment the iterator if you haven't erased an element during the current iteration. With your current code, assume that the first two elements in the map meet the erasure criterion. Then the second one will be left unerased because you increment past it on the second iteration through the loop.
for (; iterator != map.end();) {
if (iterator->second == 30 || iterator->second == 50) {
map.erase(iterator);
iterator = map.begin();
} else {
++iterator;
}
}
If your compiler supports C++11 you can do this instead to erase elements from the map
for (; iterator != map.end(); ) {
if (iterator->second == 30 || iterator->second == 50) {
iterator = map.erase(iterator);
} else {
++iterator;
}
}

Deleting a "value" from a multimap

My requirement is to delete a a "value" from the multimap and not the "key".
A key may have multiple values and i want delete a specific value.My requirement is similar to deleting a node from a linked list.
I am doing so by using multimap::erase() method.
But after deletion if I try to print the values of the multimap, the values deleted using multimap::erase() are also printed.
below is my code snippet:
void Clientqueues::clearSubscription(string name,string sessionid)
{
pair<multimap<string,string>::iterator,multimap<string,string>::iterator> i;
multimap<string, string>::iterator j;
i = registeredClientInfo.equal_range(name);
if (j == registeredClientInfo.end())
return;
for(j=i.first;j != i.second;++j)
{
if((j->second) == sessionid) registeredClientInfo.erase(j->second);
}
for(j=i.first;j != i.second;++j)
{
cout<<""<<j->second<<endl;///This prints the erased values too;
}
}
Am i doing something wrong?
Any help in this regard greatly appreciated.
Most important, you call erase(j->second), when you meant to call erase(j). You're not erasing the element of the multimap pointed to by j, you're erasing all elements whose keys are equal to the value of the element pointed to by j (which is sessionid). I expect that's nothing.
Also: call equal_range again after the erase loop is complete - the effect of using an erased iterator is undefined, so if you erased the first iterator i.first, then you can't start iterating from there again afterwards.
Note that this also means there's a bug in your loop that does the erase, since in the case that you do call erase, you increment j when it holds an iterator value that's no longer valid. Unfortunately, the correct code is:
for(j=i.first;j != i.second;)
{
if((j->second) == sessionid) {
auto next = j;
++next;
registeredClientInfo.erase(j);
j = next;
} else {
++j;
}
}
Or if you prefer:
for(j=i.first;j != i.second;)
{
auto current = j;
++j;
if((current->second) == sessionid) registeredClientInfo.erase(current);
}
Or if the entry is unique for the key/value pair, so that you only have to remove at most one thing, then:
for(j=i.first;j != i.second;++j)
{
if((j->second) == sessionid) {
registeredClientInfo.erase(j);
break;
}
}
if (j == registeredClientInfo.end()) return; isn't right either, since j is uninitialized when you do it. If the key isn't found, then equal_range returns an empty range (two equal iterator values), so your other loops will do nothing anyway.
If you deleted i.first or i.second the iterators get invalidated implying undefined behavior.